home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / Paint Shop Pro / PSP900enTR.exe / Data1.cab / _AC349965C7A54B5E92AE167DA959F028 < prev    next >
Encoding:
Text File  |  2004-08-16  |  102.2 KB  |  2,728 lines

  1. # Copyright (C) 2001,2002,2003 Python Software Foundation
  2. # email package unit tests
  3.  
  4. import os
  5. import sys
  6. import time
  7. import base64
  8. import difflib
  9. import unittest
  10. import warnings
  11. from cStringIO import StringIO
  12. from types import StringType, ListType
  13.  
  14. import email
  15.  
  16. from email.Charset import Charset
  17. from email.Header import Header, decode_header, make_header
  18. from email.Parser import Parser, HeaderParser
  19. from email.Generator import Generator, DecodedGenerator
  20. from email.Message import Message
  21. from email.MIMEAudio import MIMEAudio
  22. from email.MIMEText import MIMEText
  23. from email.MIMEImage import MIMEImage
  24. from email.MIMEBase import MIMEBase
  25. from email.MIMEMessage import MIMEMessage
  26. from email.MIMEMultipart import MIMEMultipart
  27. from email import Utils
  28. from email import Errors
  29. from email import Encoders
  30. from email import Iterators
  31. from email import base64MIME
  32. from email import quopriMIME
  33.  
  34. from test.test_support import findfile, run_unittest
  35. from email.test import __file__ as landmark
  36.  
  37.  
  38. NL = '\n'
  39. EMPTYSTRING = ''
  40. SPACE = ' '
  41.  
  42. # We don't care about DeprecationWarnings
  43. warnings.filterwarnings('ignore', '', DeprecationWarning, __name__)
  44.  
  45. try:
  46.     True, False
  47. except NameError:
  48.     True = 1
  49.     False = 0
  50.  
  51.  
  52.  
  53. def openfile(filename, mode='r'):
  54.     path = os.path.join(os.path.dirname(landmark), 'data', filename)
  55.     return open(path, mode)
  56.  
  57.  
  58.  
  59. # Base test class
  60. class TestEmailBase(unittest.TestCase):
  61.     if hasattr(difflib, 'ndiff'):
  62.         # Python 2.2 and beyond
  63.         def ndiffAssertEqual(self, first, second):
  64.             """Like failUnlessEqual except use ndiff for readable output."""
  65.             if first <> second:
  66.                 sfirst = str(first)
  67.                 ssecond = str(second)
  68.                 diff = difflib.ndiff(sfirst.splitlines(), ssecond.splitlines())
  69.                 fp = StringIO()
  70.                 print >> fp, NL, NL.join(diff)
  71.                 raise self.failureException, fp.getvalue()
  72.     else:
  73.         # Python 2.1
  74.         ndiffAssertEqual = unittest.TestCase.assertEqual
  75.  
  76.     def _msgobj(self, filename, strict=False):
  77.         fp = openfile(findfile(filename))
  78.         try:
  79.             msg = email.message_from_file(fp, strict=strict)
  80.         finally:
  81.             fp.close()
  82.         return msg
  83.  
  84.  
  85.  
  86. # Test various aspects of the Message class's API
  87. class TestMessageAPI(TestEmailBase):
  88.     def test_get_all(self):
  89.         eq = self.assertEqual
  90.         msg = self._msgobj('msg_20.txt')
  91.         eq(msg.get_all('cc'), ['ccc@zzz.org', 'ddd@zzz.org', 'eee@zzz.org'])
  92.         eq(msg.get_all('xx', 'n/a'), 'n/a')
  93.  
  94.     def test_getset_charset(self):
  95.         eq = self.assertEqual
  96.         msg = Message()
  97.         eq(msg.get_charset(), None)
  98.         charset = Charset('iso-8859-1')
  99.         msg.set_charset(charset)
  100.         eq(msg['mime-version'], '1.0')
  101.         eq(msg.get_type(), 'text/plain')
  102.         eq(msg['content-type'], 'text/plain; charset="iso-8859-1"')
  103.         eq(msg.get_param('charset'), 'iso-8859-1')
  104.         eq(msg['content-transfer-encoding'], 'quoted-printable')
  105.         eq(msg.get_charset().input_charset, 'iso-8859-1')
  106.         # Remove the charset
  107.         msg.set_charset(None)
  108.         eq(msg.get_charset(), None)
  109.         eq(msg['content-type'], 'text/plain')
  110.         # Try adding a charset when there's already MIME headers present
  111.         msg = Message()
  112.         msg['MIME-Version'] = '2.0'
  113.         msg['Content-Type'] = 'text/x-weird'
  114.         msg['Content-Transfer-Encoding'] = 'quinted-puntable'
  115.         msg.set_charset(charset)
  116.         eq(msg['mime-version'], '2.0')
  117.         eq(msg['content-type'], 'text/x-weird; charset="iso-8859-1"')
  118.         eq(msg['content-transfer-encoding'], 'quinted-puntable')
  119.  
  120.     def test_set_charset_from_string(self):
  121.         eq = self.assertEqual
  122.         msg = Message()
  123.         msg.set_charset('us-ascii')
  124.         eq(msg.get_charset().input_charset, 'us-ascii')
  125.         eq(msg['content-type'], 'text/plain; charset="us-ascii"')
  126.  
  127.     def test_set_payload_with_charset(self):
  128.         msg = Message()
  129.         charset = Charset('iso-8859-1')
  130.         msg.set_payload('This is a string payload', charset)
  131.         self.assertEqual(msg.get_charset().input_charset, 'iso-8859-1')
  132.  
  133.     def test_get_charsets(self):
  134.         eq = self.assertEqual
  135.  
  136.         msg = self._msgobj('msg_08.txt')
  137.         charsets = msg.get_charsets()
  138.         eq(charsets, [None, 'us-ascii', 'iso-8859-1', 'iso-8859-2', 'koi8-r'])
  139.  
  140.         msg = self._msgobj('msg_09.txt')
  141.         charsets = msg.get_charsets('dingbat')
  142.         eq(charsets, ['dingbat', 'us-ascii', 'iso-8859-1', 'dingbat',
  143.                       'koi8-r'])
  144.  
  145.         msg = self._msgobj('msg_12.txt')
  146.         charsets = msg.get_charsets()
  147.         eq(charsets, [None, 'us-ascii', 'iso-8859-1', None, 'iso-8859-2',
  148.                       'iso-8859-3', 'us-ascii', 'koi8-r'])
  149.  
  150.     def test_get_filename(self):
  151.         eq = self.assertEqual
  152.  
  153.         msg = self._msgobj('msg_04.txt')
  154.         filenames = [p.get_filename() for p in msg.get_payload()]
  155.         eq(filenames, ['msg.txt', 'msg.txt'])
  156.  
  157.         msg = self._msgobj('msg_07.txt')
  158.         subpart = msg.get_payload(1)
  159.         eq(subpart.get_filename(), 'dingusfish.gif')
  160.  
  161.     def test_get_boundary(self):
  162.         eq = self.assertEqual
  163.         msg = self._msgobj('msg_07.txt')
  164.         # No quotes!
  165.         eq(msg.get_boundary(), 'BOUNDARY')
  166.  
  167.     def test_set_boundary(self):
  168.         eq = self.assertEqual
  169.         # This one has no existing boundary parameter, but the Content-Type:
  170.         # header appears fifth.
  171.         msg = self._msgobj('msg_01.txt')
  172.         msg.set_boundary('BOUNDARY')
  173.         header, value = msg.items()[4]
  174.         eq(header.lower(), 'content-type')
  175.         eq(value, 'text/plain; charset="us-ascii"; boundary="BOUNDARY"')
  176.         # This one has a Content-Type: header, with a boundary, stuck in the
  177.         # middle of its headers.  Make sure the order is preserved; it should
  178.         # be fifth.
  179.         msg = self._msgobj('msg_04.txt')
  180.         msg.set_boundary('BOUNDARY')
  181.         header, value = msg.items()[4]
  182.         eq(header.lower(), 'content-type')
  183.         eq(value, 'multipart/mixed; boundary="BOUNDARY"')
  184.         # And this one has no Content-Type: header at all.
  185.         msg = self._msgobj('msg_03.txt')
  186.         self.assertRaises(Errors.HeaderParseError,
  187.                           msg.set_boundary, 'BOUNDARY')
  188.  
  189.     def test_get_decoded_payload(self):
  190.         eq = self.assertEqual
  191.         msg = self._msgobj('msg_10.txt')
  192.         # The outer message is a multipart
  193.         eq(msg.get_payload(decode=True), None)
  194.         # Subpart 1 is 7bit encoded
  195.         eq(msg.get_payload(0).get_payload(decode=True),
  196.            'This is a 7bit encoded message.\n')
  197.         # Subpart 2 is quopri
  198.         eq(msg.get_payload(1).get_payload(decode=True),
  199.            '\xa1This is a Quoted Printable encoded message!\n')
  200.         # Subpart 3 is base64
  201.         eq(msg.get_payload(2).get_payload(decode=True),
  202.            'This is a Base64 encoded message.')
  203.         # Subpart 4 has no Content-Transfer-Encoding: header.
  204.         eq(msg.get_payload(3).get_payload(decode=True),
  205.            'This has no Content-Transfer-Encoding: header.\n')
  206.  
  207.     def test_get_decoded_uu_payload(self):
  208.         eq = self.assertEqual
  209.         msg = Message()
  210.         msg.set_payload('begin 666 -\n+:&5L;&\\@=V]R;&0 \n \nend\n')
  211.         for cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
  212.             msg['content-transfer-encoding'] = cte
  213.             eq(msg.get_payload(decode=True), 'hello world')
  214.         # Now try some bogus data
  215.         msg.set_payload('foo')
  216.         eq(msg.get_payload(decode=True), 'foo')
  217.  
  218.     def test_decoded_generator(self):
  219.         eq = self.assertEqual
  220.         msg = self._msgobj('msg_07.txt')
  221.         fp = openfile('msg_17.txt')
  222.         try:
  223.             text = fp.read()
  224.         finally:
  225.             fp.close()
  226.         s = StringIO()
  227.         g = DecodedGenerator(s)
  228.         g.flatten(msg)
  229.         eq(s.getvalue(), text)
  230.  
  231.     def test__contains__(self):
  232.         msg = Message()
  233.         msg['From'] = 'Me'
  234.         msg['to'] = 'You'
  235.         # Check for case insensitivity
  236.         self.failUnless('from' in msg)
  237.         self.failUnless('From' in msg)
  238.         self.failUnless('FROM' in msg)
  239.         self.failUnless('to' in msg)
  240.         self.failUnless('To' in msg)
  241.         self.failUnless('TO' in msg)
  242.  
  243.     def test_as_string(self):
  244.         eq = self.assertEqual
  245.         msg = self._msgobj('msg_01.txt')
  246.         fp = openfile('msg_01.txt')
  247.         try:
  248.             text = fp.read()
  249.         finally:
  250.             fp.close()
  251.         eq(text, msg.as_string())
  252.         fullrepr = str(msg)
  253.         lines = fullrepr.split('\n')
  254.         self.failUnless(lines[0].startswith('From '))
  255.         eq(text, NL.join(lines[1:]))
  256.  
  257.     def test_bad_param(self):
  258.         msg = email.message_from_string("Content-Type: blarg; baz; boo\n")
  259.         self.assertEqual(msg.get_param('baz'), '')
  260.  
  261.     def test_missing_filename(self):
  262.         msg = email.message_from_string("From: foo\n")
  263.         self.assertEqual(msg.get_filename(), None)
  264.  
  265.     def test_bogus_filename(self):
  266.         msg = email.message_from_string(
  267.         "Content-Disposition: blarg; filename\n")
  268.         self.assertEqual(msg.get_filename(), '')
  269.  
  270.     def test_missing_boundary(self):
  271.         msg = email.message_from_string("From: foo\n")
  272.         self.assertEqual(msg.get_boundary(), None)
  273.  
  274.     def test_get_params(self):
  275.         eq = self.assertEqual
  276.         msg = email.message_from_string(
  277.             'X-Header: foo=one; bar=two; baz=three\n')
  278.         eq(msg.get_params(header='x-header'),
  279.            [('foo', 'one'), ('bar', 'two'), ('baz', 'three')])
  280.         msg = email.message_from_string(
  281.             'X-Header: foo; bar=one; baz=two\n')
  282.         eq(msg.get_params(header='x-header'),
  283.            [('foo', ''), ('bar', 'one'), ('baz', 'two')])
  284.         eq(msg.get_params(), None)
  285.         msg = email.message_from_string(
  286.             'X-Header: foo; bar="one"; baz=two\n')
  287.         eq(msg.get_params(header='x-header'),
  288.            [('foo', ''), ('bar', 'one'), ('baz', 'two')])
  289.  
  290.     def test_get_param_liberal(self):
  291.         msg = Message()
  292.         msg['Content-Type'] = 'Content-Type: Multipart/mixed; boundary = "CPIMSSMTPC06p5f3tG"'
  293.         self.assertEqual(msg.get_param('boundary'), 'CPIMSSMTPC06p5f3tG')
  294.  
  295.     def test_get_param(self):
  296.         eq = self.assertEqual
  297.         msg = email.message_from_string(
  298.             "X-Header: foo=one; bar=two; baz=three\n")
  299.         eq(msg.get_param('bar', header='x-header'), 'two')
  300.         eq(msg.get_param('quuz', header='x-header'), None)
  301.         eq(msg.get_param('quuz'), None)
  302.         msg = email.message_from_string(
  303.             'X-Header: foo; bar="one"; baz=two\n')
  304.         eq(msg.get_param('foo', header='x-header'), '')
  305.         eq(msg.get_param('bar', header='x-header'), 'one')
  306.         eq(msg.get_param('baz', header='x-header'), 'two')
  307.         # XXX: We are not RFC-2045 compliant!  We cannot parse:
  308.         # msg["Content-Type"] = 'text/plain; weird="hey; dolly? [you] @ <\\"home\\">?"'
  309.         # msg.get_param("weird")
  310.         # yet.
  311.  
  312.     def test_get_param_funky_continuation_lines(self):
  313.         msg = self._msgobj('msg_22.txt')
  314.         self.assertEqual(msg.get_payload(1).get_param('name'), 'wibble.JPG')
  315.  
  316.     def test_get_param_with_semis_in_quotes(self):
  317.         msg = email.message_from_string(
  318.             'Content-Type: image/pjpeg; name="Jim&&Jill"\n')
  319.         self.assertEqual(msg.get_param('name'), 'Jim&&Jill')
  320.         self.assertEqual(msg.get_param('name', unquote=False),
  321.                          '"Jim&&Jill"')
  322.  
  323.     def test_has_key(self):
  324.         msg = email.message_from_string('Header: exists')
  325.         self.failUnless(msg.has_key('header'))
  326.         self.failUnless(msg.has_key('Header'))
  327.         self.failUnless(msg.has_key('HEADER'))
  328.         self.failIf(msg.has_key('headeri'))
  329.  
  330.     def test_set_param(self):
  331.         eq = self.assertEqual
  332.         msg = Message()
  333.         msg.set_param('charset', 'iso-2022-jp')
  334.         eq(msg.get_param('charset'), 'iso-2022-jp')
  335.         msg.set_param('importance', 'high value')
  336.         eq(msg.get_param('importance'), 'high value')
  337.         eq(msg.get_param('importance', unquote=False), '"high value"')
  338.         eq(msg.get_params(), [('text/plain', ''),
  339.                               ('charset', 'iso-2022-jp'),
  340.                               ('importance', 'high value')])
  341.         eq(msg.get_params(unquote=False), [('text/plain', ''),
  342.                                        ('charset', '"iso-2022-jp"'),
  343.                                        ('importance', '"high value"')])
  344.         msg.set_param('charset', 'iso-9999-xx', header='X-Jimmy')
  345.         eq(msg.get_param('charset', header='X-Jimmy'), 'iso-9999-xx')
  346.  
  347.     def test_del_param(self):
  348.         eq = self.assertEqual
  349.         msg = self._msgobj('msg_05.txt')
  350.         eq(msg.get_params(),
  351.            [('multipart/report', ''), ('report-type', 'delivery-status'),
  352.             ('boundary', 'D1690A7AC1.996856090/mail.example.com')])
  353.         old_val = msg.get_param("report-type")
  354.         msg.del_param("report-type")
  355.         eq(msg.get_params(),
  356.            [('multipart/report', ''),
  357.             ('boundary', 'D1690A7AC1.996856090/mail.example.com')])
  358.         msg.set_param("report-type", old_val)
  359.         eq(msg.get_params(),
  360.            [('multipart/report', ''),
  361.             ('boundary', 'D1690A7AC1.996856090/mail.example.com'),
  362.             ('report-type', old_val)])
  363.  
  364.     def test_set_type(self):
  365.         eq = self.assertEqual
  366.         msg = Message()
  367.         self.assertRaises(ValueError, msg.set_type, 'text')
  368.         msg.set_type('text/plain')
  369.         eq(msg['content-type'], 'text/plain')
  370.         msg.set_param('charset', 'us-ascii')
  371.         eq(msg['content-type'], 'text/plain; charset="us-ascii"')
  372.         msg.set_type('text/html')
  373.         eq(msg['content-type'], 'text/html; charset="us-ascii"')
  374.  
  375.     def test_get_content_type_missing(self):
  376.         msg = Message()
  377.         self.assertEqual(msg.get_content_type(), 'text/plain')
  378.  
  379.     def test_get_content_type_missing_with_default_type(self):
  380.         msg = Message()
  381.         msg.set_default_type('message/rfc822')
  382.         self.assertEqual(msg.get_content_type(), 'message/rfc822')
  383.  
  384.     def test_get_content_type_from_message_implicit(self):
  385.         msg = self._msgobj('msg_30.txt')
  386.         self.assertEqual(msg.get_payload(0).get_content_type(),
  387.                          'message/rfc822')
  388.  
  389.     def test_get_content_type_from_message_explicit(self):
  390.         msg = self._msgobj('msg_28.txt')
  391.         self.assertEqual(msg.get_payload(0).get_content_type(),
  392.                          'message/rfc822')
  393.  
  394.     def test_get_content_type_from_message_text_plain_implicit(self):
  395.         msg = self._msgobj('msg_03.txt')
  396.         self.assertEqual(msg.get_content_type(), 'text/plain')
  397.  
  398.     def test_get_content_type_from_message_text_plain_explicit(self):
  399.         msg = self._msgobj('msg_01.txt')
  400.         self.assertEqual(msg.get_content_type(), 'text/plain')
  401.  
  402.     def test_get_content_maintype_missing(self):
  403.         msg = Message()
  404.         self.assertEqual(msg.get_content_maintype(), 'text')
  405.  
  406.     def test_get_content_maintype_missing_with_default_type(self):
  407.         msg = Message()
  408.         msg.set_default_type('message/rfc822')
  409.         self.assertEqual(msg.get_content_maintype(), 'message')
  410.  
  411.     def test_get_content_maintype_from_message_implicit(self):
  412.         msg = self._msgobj('msg_30.txt')
  413.         self.assertEqual(msg.get_payload(0).get_content_maintype(), 'message')
  414.  
  415.     def test_get_content_maintype_from_message_explicit(self):
  416.         msg = self._msgobj('msg_28.txt')
  417.         self.assertEqual(msg.get_payload(0).get_content_maintype(), 'message')
  418.  
  419.     def test_get_content_maintype_from_message_text_plain_implicit(self):
  420.         msg = self._msgobj('msg_03.txt')
  421.         self.assertEqual(msg.get_content_maintype(), 'text')
  422.  
  423.     def test_get_content_maintype_from_message_text_plain_explicit(self):
  424.         msg = self._msgobj('msg_01.txt')
  425.         self.assertEqual(msg.get_content_maintype(), 'text')
  426.  
  427.     def test_get_content_subtype_missing(self):
  428.         msg = Message()
  429.         self.assertEqual(msg.get_content_subtype(), 'plain')
  430.  
  431.     def test_get_content_subtype_missing_with_default_type(self):
  432.         msg = Message()
  433.         msg.set_default_type('message/rfc822')
  434.         self.assertEqual(msg.get_content_subtype(), 'rfc822')
  435.  
  436.     def test_get_content_subtype_from_message_implicit(self):
  437.         msg = self._msgobj('msg_30.txt')
  438.         self.assertEqual(msg.get_payload(0).get_content_subtype(), 'rfc822')
  439.  
  440.     def test_get_content_subtype_from_message_explicit(self):
  441.         msg = self._msgobj('msg_28.txt')
  442.         self.assertEqual(msg.get_payload(0).get_content_subtype(), 'rfc822')
  443.  
  444.     def test_get_content_subtype_from_message_text_plain_implicit(self):
  445.         msg = self._msgobj('msg_03.txt')
  446.         self.assertEqual(msg.get_content_subtype(), 'plain')
  447.  
  448.     def test_get_content_subtype_from_message_text_plain_explicit(self):
  449.         msg = self._msgobj('msg_01.txt')
  450.         self.assertEqual(msg.get_content_subtype(), 'plain')
  451.  
  452.     def test_get_content_maintype_error(self):
  453.         msg = Message()
  454.         msg['Content-Type'] = 'no-slash-in-this-string'
  455.         self.assertEqual(msg.get_content_maintype(), 'text')
  456.  
  457.     def test_get_content_subtype_error(self):
  458.         msg = Message()
  459.         msg['Content-Type'] = 'no-slash-in-this-string'
  460.         self.assertEqual(msg.get_content_subtype(), 'plain')
  461.  
  462.     def test_replace_header(self):
  463.         eq = self.assertEqual
  464.         msg = Message()
  465.         msg.add_header('First', 'One')
  466.         msg.add_header('Second', 'Two')
  467.         msg.add_header('Third', 'Three')
  468.         eq(msg.keys(), ['First', 'Second', 'Third'])
  469.         eq(msg.values(), ['One', 'Two', 'Three'])
  470.         msg.replace_header('Second', 'Twenty')
  471.         eq(msg.keys(), ['First', 'Second', 'Third'])
  472.         eq(msg.values(), ['One', 'Twenty', 'Three'])
  473.         msg.add_header('First', 'Eleven')
  474.         msg.replace_header('First', 'One Hundred')
  475.         eq(msg.keys(), ['First', 'Second', 'Third', 'First'])
  476.         eq(msg.values(), ['One Hundred', 'Twenty', 'Three', 'Eleven'])
  477.         self.assertRaises(KeyError, msg.replace_header, 'Fourth', 'Missing')
  478.  
  479.     def test_broken_base64_payload(self):
  480.         x = 'AwDp0P7//y6LwKEAcPa/6Q=9'
  481.         msg = Message()
  482.         msg['content-type'] = 'audio/x-midi'
  483.         msg['content-transfer-encoding'] = 'base64'
  484.         msg.set_payload(x)
  485.         self.assertEqual(msg.get_payload(decode=True), x)
  486.  
  487.  
  488.  
  489. # Test the email.Encoders module
  490. class TestEncoders(unittest.TestCase):
  491.     def test_encode_noop(self):
  492.         eq = self.assertEqual
  493.         msg = MIMEText('hello world', _encoder=Encoders.encode_noop)
  494.         eq(msg.get_payload(), 'hello world')
  495.  
  496.     def test_encode_7bit(self):
  497.         eq = self.assertEqual
  498.         msg = MIMEText('hello world', _encoder=Encoders.encode_7or8bit)
  499.         eq(msg.get_payload(), 'hello world')
  500.         eq(msg['content-transfer-encoding'], '7bit')
  501.         msg = MIMEText('hello \x7f world', _encoder=Encoders.encode_7or8bit)
  502.         eq(msg.get_payload(), 'hello \x7f world')
  503.         eq(msg['content-transfer-encoding'], '7bit')
  504.  
  505.     def test_encode_8bit(self):
  506.         eq = self.assertEqual
  507.         msg = MIMEText('hello \x80 world', _encoder=Encoders.encode_7or8bit)
  508.         eq(msg.get_payload(), 'hello \x80 world')
  509.         eq(msg['content-transfer-encoding'], '8bit')
  510.  
  511.     def test_encode_empty_payload(self):
  512.         eq = self.assertEqual
  513.         msg = Message()
  514.         msg.set_charset('us-ascii')
  515.         eq(msg['content-transfer-encoding'], '7bit')
  516.  
  517.     def test_encode_base64(self):
  518.         eq = self.assertEqual
  519.         msg = MIMEText('hello world', _encoder=Encoders.encode_base64)
  520.         eq(msg.get_payload(), 'aGVsbG8gd29ybGQ=')
  521.         eq(msg['content-transfer-encoding'], 'base64')
  522.  
  523.     def test_encode_quoted_printable(self):
  524.         eq = self.assertEqual
  525.         msg = MIMEText('hello world', _encoder=Encoders.encode_quopri)
  526.         eq(msg.get_payload(), 'hello=20world')
  527.         eq(msg['content-transfer-encoding'], 'quoted-printable')
  528.  
  529.     def test_default_cte(self):
  530.         eq = self.assertEqual
  531.         msg = MIMEText('hello world')
  532.         eq(msg['content-transfer-encoding'], '7bit')
  533.  
  534.     def test_default_cte(self):
  535.         eq = self.assertEqual
  536.         # With no explicit _charset its us-ascii, and all are 7-bit
  537.         msg = MIMEText('hello world')
  538.         eq(msg['content-transfer-encoding'], '7bit')
  539.         # Similar, but with 8-bit data
  540.         msg = MIMEText('hello \xf8 world')
  541.         eq(msg['content-transfer-encoding'], '8bit')
  542.         # And now with a different charset
  543.         msg = MIMEText('hello \xf8 world', _charset='iso-8859-1')
  544.         eq(msg['content-transfer-encoding'], 'quoted-printable')
  545.  
  546.  
  547.  
  548. # Test long header wrapping
  549. class TestLongHeaders(TestEmailBase):
  550.     def test_split_long_continuation(self):
  551.         eq = self.ndiffAssertEqual
  552.         msg = email.message_from_string("""\
  553. Subject: bug demonstration
  554. \t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
  555. \tmore text
  556.  
  557. test
  558. """)
  559.         sfp = StringIO()
  560.         g = Generator(sfp)
  561.         g.flatten(msg)
  562.         eq(sfp.getvalue(), """\
  563. Subject: bug demonstration
  564. \t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
  565. \tmore text
  566.  
  567. test
  568. """)
  569.  
  570.     def test_another_long_almost_unsplittable_header(self):
  571.         eq = self.ndiffAssertEqual
  572.         hstr = """\
  573. bug demonstration
  574. \t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
  575. \tmore text"""
  576.         h = Header(hstr, continuation_ws='\t')
  577.         eq(h.encode(), """\
  578. bug demonstration
  579. \t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
  580. \tmore text""")
  581.         h = Header(hstr)
  582.         eq(h.encode(), """\
  583. bug demonstration
  584.  12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789
  585.  more text""")
  586.  
  587.     def test_long_nonstring(self):
  588.         eq = self.ndiffAssertEqual
  589.         g = Charset("iso-8859-1")
  590.         cz = Charset("iso-8859-2")
  591.         utf8 = Charset("utf-8")
  592.         g_head = "Die Mieter treten hier ein werden mit einem Foerderband komfortabel den Korridor entlang, an s\xfcdl\xfcndischen Wandgem\xe4lden vorbei, gegen die rotierenden Klingen bef\xf6rdert. "
  593.         cz_head = "Finan\xe8ni metropole se hroutily pod tlakem jejich d\xf9vtipu.. "
  594.         utf8_head = u"\u6b63\u78ba\u306b\u8a00\u3046\u3068\u7ffb\u8a33\u306f\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4e00\u90e8\u306f\u30c9\u30a4\u30c4\u8a9e\u3067\u3059\u304c\u3001\u3042\u3068\u306f\u3067\u305f\u3089\u3081\u3067\u3059\u3002\u5b9f\u969b\u306b\u306f\u300cWenn ist das Nunstuck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput.\u300d\u3068\u8a00\u3063\u3066\u3044\u307e\u3059\u3002".encode("utf-8")
  595.         h = Header(g_head, g, header_name='Subject')
  596.         h.append(cz_head, cz)
  597.         h.append(utf8_head, utf8)
  598.         msg = Message()
  599.         msg['Subject'] = h
  600.         sfp = StringIO()
  601.         g = Generator(sfp)
  602.         g.flatten(msg)
  603.         eq(sfp.getvalue(), """\
  604. Subject: =?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerd?=
  605.  =?iso-8859-1?q?erband_komfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndi?=
  606.  =?iso-8859-1?q?schen_Wandgem=E4lden_vorbei=2C_gegen_die_rotierenden_Kling?=
  607.  =?iso-8859-1?q?en_bef=F6rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_met?=
  608.  =?iso-8859-2?q?ropole_se_hroutily_pod_tlakem_jejich_d=F9vtipu=2E=2E_?=
  609.  =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE?=
  610.  =?utf-8?b?44G+44Gb44KT44CC5LiA6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB?=
  611.  =?utf-8?b?44GC44Go44Gv44Gn44Gf44KJ44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CM?=
  612.  =?utf-8?q?Wenn_ist_das_Nunstuck_git_und_Slotermeyer=3F_Ja!_Beiherhund_das?=
  613.  =?utf-8?b?IE9kZXIgZGllIEZsaXBwZXJ3YWxkdCBnZXJzcHV0LuOAjeOBqOiogOOBow==?=
  614.  =?utf-8?b?44Gm44GE44G+44GZ44CC?=
  615.  
  616. """)
  617.         eq(h.encode(), """\
  618. =?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerd?=
  619.  =?iso-8859-1?q?erband_komfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndi?=
  620.  =?iso-8859-1?q?schen_Wandgem=E4lden_vorbei=2C_gegen_die_rotierenden_Kling?=
  621.  =?iso-8859-1?q?en_bef=F6rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_met?=
  622.  =?iso-8859-2?q?ropole_se_hroutily_pod_tlakem_jejich_d=F9vtipu=2E=2E_?=
  623.  =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE?=
  624.  =?utf-8?b?44G+44Gb44KT44CC5LiA6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB?=
  625.  =?utf-8?b?44GC44Go44Gv44Gn44Gf44KJ44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CM?=
  626.  =?utf-8?q?Wenn_ist_das_Nunstuck_git_und_Slotermeyer=3F_Ja!_Beiherhund_das?=
  627.  =?utf-8?b?IE9kZXIgZGllIEZsaXBwZXJ3YWxkdCBnZXJzcHV0LuOAjeOBqOiogOOBow==?=
  628.  =?utf-8?b?44Gm44GE44G+44GZ44CC?=""")
  629.  
  630.     def test_long_header_encode(self):
  631.         eq = self.ndiffAssertEqual
  632.         h = Header('wasnipoop; giraffes="very-long-necked-animals"; '
  633.                    'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"',
  634.                    header_name='X-Foobar-Spoink-Defrobnit')
  635.         eq(h.encode(), '''\
  636. wasnipoop; giraffes="very-long-necked-animals";
  637.  spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''')
  638.  
  639.     def test_long_header_encode_with_tab_continuation(self):
  640.         eq = self.ndiffAssertEqual
  641.         h = Header('wasnipoop; giraffes="very-long-necked-animals"; '
  642.                    'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"',
  643.                    header_name='X-Foobar-Spoink-Defrobnit',
  644.                    continuation_ws='\t')
  645.         eq(h.encode(), '''\
  646. wasnipoop; giraffes="very-long-necked-animals";
  647. \tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''')
  648.  
  649.     def test_header_splitter(self):
  650.         eq = self.ndiffAssertEqual
  651.         msg = MIMEText('')
  652.         # It'd be great if we could use add_header() here, but that doesn't
  653.         # guarantee an order of the parameters.
  654.         msg['X-Foobar-Spoink-Defrobnit'] = (
  655.             'wasnipoop; giraffes="very-long-necked-animals"; '
  656.             'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"')
  657.         sfp = StringIO()
  658.         g = Generator(sfp)
  659.         g.flatten(msg)
  660.         eq(sfp.getvalue(), '''\
  661. Content-Type: text/plain; charset="us-ascii"
  662. MIME-Version: 1.0
  663. Content-Transfer-Encoding: 7bit
  664. X-Foobar-Spoink-Defrobnit: wasnipoop; giraffes="very-long-necked-animals";
  665. \tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey"
  666.  
  667. ''')
  668.  
  669.     def test_no_semis_header_splitter(self):
  670.         eq = self.ndiffAssertEqual
  671.         msg = Message()
  672.         msg['From'] = 'test@dom.ain'
  673.         msg['References'] = SPACE.join(['<%d@dom.ain>' % i for i in range(10)])
  674.         msg.set_payload('Test')
  675.         sfp = StringIO()
  676.         g = Generator(sfp)
  677.         g.flatten(msg)
  678.         eq(sfp.getvalue(), """\
  679. From: test@dom.ain
  680. References: <0@dom.ain> <1@dom.ain> <2@dom.ain> <3@dom.ain> <4@dom.ain>
  681. \t<5@dom.ain> <6@dom.ain> <7@dom.ain> <8@dom.ain> <9@dom.ain>
  682.  
  683. Test""")
  684.  
  685.     def test_no_split_long_header(self):
  686.         eq = self.ndiffAssertEqual
  687.         hstr = 'References: ' + 'x' * 80
  688.         h = Header(hstr, continuation_ws='\t')
  689.         eq(h.encode(), """\
  690. References: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx""")
  691.  
  692.     def test_splitting_multiple_long_lines(self):
  693.         eq = self.ndiffAssertEqual
  694.         hstr = """\
  695. from babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for <mailman-admin@babylon.socal-raves.org>; Sat, 2 Feb 2002 17:00:06 -0800 (PST)
  696. \tfrom babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for <mailman-admin@babylon.socal-raves.org>; Sat, 2 Feb 2002 17:00:06 -0800 (PST)
  697. \tfrom babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for <mailman-admin@babylon.socal-raves.org>; Sat, 2 Feb 2002 17:00:06 -0800 (PST)
  698. """
  699.         h = Header(hstr, continuation_ws='\t')
  700.         eq(h.encode(), """\
  701. from babylon.socal-raves.org (localhost [127.0.0.1]);
  702. \tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81;
  703. \tfor <mailman-admin@babylon.socal-raves.org>;
  704. \tSat, 2 Feb 2002 17:00:06 -0800 (PST)
  705. \tfrom babylon.socal-raves.org (localhost [127.0.0.1]);
  706. \tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81;
  707. \tfor <mailman-admin@babylon.socal-raves.org>;
  708. \tSat, 2 Feb 2002 17:00:06 -0800 (PST)
  709. \tfrom babylon.socal-raves.org (localhost [127.0.0.1]);
  710. \tby babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81;
  711. \tfor <mailman-admin@babylon.socal-raves.org>;
  712. \tSat, 2 Feb 2002 17:00:06 -0800 (PST)""")
  713.  
  714.     def test_splitting_first_line_only_is_long(self):
  715.         eq = self.ndiffAssertEqual
  716.         hstr = """\
  717. from modemcable093.139-201-24.que.mc.videotron.ca ([24.201.139.93] helo=cthulhu.gerg.ca)
  718. \tby kronos.mems-exchange.org with esmtp (Exim 4.05)
  719. \tid 17k4h5-00034i-00
  720. \tfor test@mems-exchange.org; Wed, 28 Aug 2002 11:25:20 -0400"""
  721.         h = Header(hstr, maxlinelen=78, header_name='Received',
  722.                    continuation_ws='\t')
  723.         eq(h.encode(), """\
  724. from modemcable093.139-201-24.que.mc.videotron.ca ([24.201.139.93]
  725. \thelo=cthulhu.gerg.ca)
  726. \tby kronos.mems-exchange.org with esmtp (Exim 4.05)
  727. \tid 17k4h5-00034i-00
  728. \tfor test@mems-exchange.org; Wed, 28 Aug 2002 11:25:20 -0400""")
  729.  
  730.     def test_long_8bit_header(self):
  731.         eq = self.ndiffAssertEqual
  732.         msg = Message()
  733.         h = Header('Britische Regierung gibt', 'iso-8859-1',
  734.                     header_name='Subject')
  735.         h.append('gr\xfcnes Licht f\xfcr Offshore-Windkraftprojekte')
  736.         msg['Subject'] = h
  737.         eq(msg.as_string(), """\
  738. Subject: =?iso-8859-1?q?Britische_Regierung_gibt?= =?iso-8859-1?q?gr=FCnes?=
  739.  =?iso-8859-1?q?_Licht_f=FCr_Offshore-Windkraftprojekte?=
  740.  
  741. """)
  742.  
  743.     def test_long_8bit_header_no_charset(self):
  744.         eq = self.ndiffAssertEqual
  745.         msg = Message()
  746.         msg['Reply-To'] = 'Britische Regierung gibt gr\xfcnes Licht f\xfcr Offshore-Windkraftprojekte <a-very-long-address@example.com>'
  747.         eq(msg.as_string(), """\
  748. Reply-To: Britische Regierung gibt gr\xfcnes Licht f\xfcr Offshore-Windkraftprojekte <a-very-long-address@example.com>
  749.  
  750. """)
  751.  
  752.     def test_long_to_header(self):
  753.         eq = self.ndiffAssertEqual
  754.         to = '"Someone Test #A" <someone@eecs.umich.edu>,<someone@eecs.umich.edu>,"Someone Test #B" <someone@umich.edu>, "Someone Test #C" <someone@eecs.umich.edu>, "Someone Test #D" <someone@eecs.umich.edu>'
  755.         msg = Message()
  756.         msg['To'] = to
  757.         eq(msg.as_string(0), '''\
  758. To: "Someone Test #A" <someone@eecs.umich.edu>, <someone@eecs.umich.edu>,
  759. \t"Someone Test #B" <someone@umich.edu>,
  760. \t"Someone Test #C" <someone@eecs.umich.edu>,
  761. \t"Someone Test #D" <someone@eecs.umich.edu>
  762.  
  763. ''')
  764.  
  765.     def test_long_line_after_append(self):
  766.         eq = self.ndiffAssertEqual
  767.         s = 'This is an example of string which has almost the limit of header length.'
  768.         h = Header(s)
  769.         h.append('Add another line.')
  770.         eq(h.encode(), """\
  771. This is an example of string which has almost the limit of header length.
  772.  Add another line.""")
  773.  
  774.     def test_shorter_line_with_append(self):
  775.         eq = self.ndiffAssertEqual
  776.         s = 'This is a shorter line.'
  777.         h = Header(s)
  778.         h.append('Add another sentence. (Surprise?)')
  779.         eq(h.encode(),
  780.            'This is a shorter line. Add another sentence. (Surprise?)')
  781.  
  782.     def test_long_field_name(self):
  783.         eq = self.ndiffAssertEqual
  784.         fn = 'X-Very-Very-Very-Long-Header-Name'
  785.         gs = "Die Mieter treten hier ein werden mit einem Foerderband komfortabel den Korridor entlang, an s\xfcdl\xfcndischen Wandgem\xe4lden vorbei, gegen die rotierenden Klingen bef\xf6rdert. "
  786.         h = Header(gs, 'iso-8859-1', header_name=fn)
  787.         # BAW: this seems broken because the first line is too long
  788.         eq(h.encode(), """\
  789. =?iso-8859-1?q?Die_Mieter_treten_hier_?=
  790.  =?iso-8859-1?q?ein_werden_mit_einem_Foerderband_komfortabel_den_Korridor_?=
  791.  =?iso-8859-1?q?entlang=2C_an_s=FCdl=FCndischen_Wandgem=E4lden_vorbei=2C_g?=
  792.  =?iso-8859-1?q?egen_die_rotierenden_Klingen_bef=F6rdert=2E_?=""")
  793.  
  794.     def test_long_received_header(self):
  795.         h = 'from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by hrothgar.la.mastaler.com (tmda-ofmipd) with ESMTP; Wed, 05 Mar 2003 18:10:18 -0700'
  796.         msg = Message()
  797.         msg['Received-1'] = Header(h, continuation_ws='\t')
  798.         msg['Received-2'] = h
  799.         self.assertEqual(msg.as_string(), """\
  800. Received-1: from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by
  801. \throthgar.la.mastaler.com (tmda-ofmipd) with ESMTP;
  802. \tWed, 05 Mar 2003 18:10:18 -0700
  803. Received-2: from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by
  804. \throthgar.la.mastaler.com (tmda-ofmipd) with ESMTP;
  805. \tWed, 05 Mar 2003 18:10:18 -0700
  806.  
  807. """)
  808.  
  809.     def test_string_headerinst_eq(self):
  810.         h = '<15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de> (David Bremner\'s message of "Thu, 6 Mar 2003 13:58:21 +0100")'
  811.         msg = Message()
  812.         msg['Received-1'] = Header(h, header_name='Received-1',
  813.                                    continuation_ws='\t')
  814.         msg['Received-2'] = h
  815.         self.assertEqual(msg.as_string(), """\
  816. Received-1: <15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de>
  817. \t(David Bremner's message of "Thu, 6 Mar 2003 13:58:21 +0100")
  818. Received-2: <15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de>
  819. \t(David Bremner's message of "Thu, 6 Mar 2003 13:58:21 +0100")
  820.  
  821. """)
  822.  
  823.     def test_long_unbreakable_lines_with_continuation(self):
  824.         eq = self.ndiffAssertEqual
  825.         msg = Message()
  826.         t = """\
  827.  iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9
  828.  locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp"""
  829.         msg['Face-1'] = t
  830.         msg['Face-2'] = Header(t, header_name='Face-2')
  831.         eq(msg.as_string(), """\
  832. Face-1: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9
  833. \tlocQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp
  834. Face-2: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9
  835.  locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp
  836.  
  837. """)
  838.  
  839.     def test_another_long_multiline_header(self):
  840.         eq = self.ndiffAssertEqual
  841.         m = '''\
  842. Received: from siimage.com ([172.25.1.3]) by zima.siliconimage.com with Microsoft SMTPSVC(5.0.2195.4905);
  843. \tWed, 16 Oct 2002 07:41:11 -0700'''
  844.         msg = email.message_from_string(m)
  845.         eq(msg.as_string(), '''\
  846. Received: from siimage.com ([172.25.1.3]) by zima.siliconimage.com with
  847. \tMicrosoft SMTPSVC(5.0.2195.4905); Wed, 16 Oct 2002 07:41:11 -0700
  848.  
  849. ''')
  850.  
  851.     def test_long_lines_with_different_header(self):
  852.         eq = self.ndiffAssertEqual
  853.         h = """\
  854. List-Unsubscribe: <https://lists.sourceforge.net/lists/listinfo/spamassassin-talk>,
  855.         <mailto:spamassassin-talk-request@lists.sourceforge.net?subject=unsubscribe>"""
  856.         msg = Message()
  857.         msg['List'] = h
  858.         msg['List'] = Header(h, header_name='List')
  859.         eq(msg.as_string(), """\
  860. List: List-Unsubscribe: <https://lists.sourceforge.net/lists/listinfo/spamassassin-talk>,
  861. \t<mailto:spamassassin-talk-request@lists.sourceforge.net?subject=unsubscribe>
  862. List: List-Unsubscribe: <https://lists.sourceforge.net/lists/listinfo/spamassassin-talk>,
  863.  <mailto:spamassassin-talk-request@lists.sourceforge.net?subject=unsubscribe>
  864.  
  865. """)
  866.  
  867.  
  868.  
  869. # Test mangling of "From " lines in the body of a message
  870. class TestFromMangling(unittest.TestCase):
  871.     def setUp(self):
  872.         self.msg = Message()
  873.         self.msg['From'] = 'aaa@bbb.org'
  874.         self.msg.set_payload("""\
  875. From the desk of A.A.A.:
  876. Blah blah blah
  877. """)
  878.  
  879.     def test_mangled_from(self):
  880.         s = StringIO()
  881.         g = Generator(s, mangle_from_=True)
  882.         g.flatten(self.msg)
  883.         self.assertEqual(s.getvalue(), """\
  884. From: aaa@bbb.org
  885.  
  886. >From the desk of A.A.A.:
  887. Blah blah blah
  888. """)
  889.  
  890.     def test_dont_mangle_from(self):
  891.         s = StringIO()
  892.         g = Generator(s, mangle_from_=False)
  893.         g.flatten(self.msg)
  894.         self.assertEqual(s.getvalue(), """\
  895. From: aaa@bbb.org
  896.  
  897. From the desk of A.A.A.:
  898. Blah blah blah
  899. """)
  900.  
  901.  
  902.  
  903. # Test the basic MIMEAudio class
  904. class TestMIMEAudio(unittest.TestCase):
  905.     def setUp(self):
  906.         # Make sure we pick up the audiotest.au that lives in email/test/data.
  907.         # In Python, there's an audiotest.au living in Lib/test but that isn't
  908.         # included in some binary distros that don't include the test
  909.         # package.  The trailing empty string on the .join() is significant
  910.         # since findfile() will do a dirname().
  911.         datadir = os.path.join(os.path.dirname(landmark), 'data', '')
  912.         fp = open(findfile('audiotest.au', datadir), 'rb')
  913.         try:
  914.             self._audiodata = fp.read()
  915.         finally:
  916.             fp.close()
  917.         self._au = MIMEAudio(self._audiodata)
  918.  
  919.     def test_guess_minor_type(self):
  920.         self.assertEqual(self._au.get_type(), 'audio/basic')
  921.  
  922.     def test_encoding(self):
  923.         payload = self._au.get_payload()
  924.         self.assertEqual(base64.decodestring(payload), self._audiodata)
  925.  
  926.     def checkSetMinor(self):
  927.         au = MIMEAudio(self._audiodata, 'fish')
  928.         self.assertEqual(im.get_type(), 'audio/fish')
  929.  
  930.     def test_custom_encoder(self):
  931.         eq = self.assertEqual
  932.         def encoder(msg):
  933.             orig = msg.get_payload()
  934.             msg.set_payload(0)
  935.             msg['Content-Transfer-Encoding'] = 'broken64'
  936.         au = MIMEAudio(self._audiodata, _encoder=encoder)
  937.         eq(au.get_payload(), 0)
  938.         eq(au['content-transfer-encoding'], 'broken64')
  939.  
  940.     def test_add_header(self):
  941.         eq = self.assertEqual
  942.         unless = self.failUnless
  943.         self._au.add_header('Content-Disposition', 'attachment',
  944.                             filename='audiotest.au')
  945.         eq(self._au['content-disposition'],
  946.            'attachment; filename="audiotest.au"')
  947.         eq(self._au.get_params(header='content-disposition'),
  948.            [('attachment', ''), ('filename', 'audiotest.au')])
  949.         eq(self._au.get_param('filename', header='content-disposition'),
  950.            'audiotest.au')
  951.         missing = []
  952.         eq(self._au.get_param('attachment', header='content-disposition'), '')
  953.         unless(self._au.get_param('foo', failobj=missing,
  954.                                   header='content-disposition') is missing)
  955.         # Try some missing stuff
  956.         unless(self._au.get_param('foobar', missing) is missing)
  957.         unless(self._au.get_param('attachment', missing,
  958.                                   header='foobar') is missing)
  959.  
  960.  
  961.  
  962. # Test the basic MIMEImage class
  963. class TestMIMEImage(unittest.TestCase):
  964.     def setUp(self):
  965.         fp = openfile('PyBanner048.gif')
  966.         try:
  967.             self._imgdata = fp.read()
  968.         finally:
  969.             fp.close()
  970.         self._im = MIMEImage(self._imgdata)
  971.  
  972.     def test_guess_minor_type(self):
  973.         self.assertEqual(self._im.get_type(), 'image/gif')
  974.  
  975.     def test_encoding(self):
  976.         payload = self._im.get_payload()
  977.         self.assertEqual(base64.decodestring(payload), self._imgdata)
  978.  
  979.     def checkSetMinor(self):
  980.         im = MIMEImage(self._imgdata, 'fish')
  981.         self.assertEqual(im.get_type(), 'image/fish')
  982.  
  983.     def test_custom_encoder(self):
  984.         eq = self.assertEqual
  985.         def encoder(msg):
  986.             orig = msg.get_payload()
  987.             msg.set_payload(0)
  988.             msg['Content-Transfer-Encoding'] = 'broken64'
  989.         im = MIMEImage(self._imgdata, _encoder=encoder)
  990.         eq(im.get_payload(), 0)
  991.         eq(im['content-transfer-encoding'], 'broken64')
  992.  
  993.     def test_add_header(self):
  994.         eq = self.assertEqual
  995.         unless = self.failUnless
  996.         self._im.add_header('Content-Disposition', 'attachment',
  997.                             filename='dingusfish.gif')
  998.         eq(self._im['content-disposition'],
  999.            'attachment; filename="dingusfish.gif"')
  1000.         eq(self._im.get_params(header='content-disposition'),
  1001.            [('attachment', ''), ('filename', 'dingusfish.gif')])
  1002.         eq(self._im.get_param('filename', header='content-disposition'),
  1003.            'dingusfish.gif')
  1004.         missing = []
  1005.         eq(self._im.get_param('attachment', header='content-disposition'), '')
  1006.         unless(self._im.get_param('foo', failobj=missing,
  1007.                                   header='content-disposition') is missing)
  1008.         # Try some missing stuff
  1009.         unless(self._im.get_param('foobar', missing) is missing)
  1010.         unless(self._im.get_param('attachment', missing,
  1011.                                   header='foobar') is missing)
  1012.  
  1013.  
  1014.  
  1015. # Test the basic MIMEText class
  1016. class TestMIMEText(unittest.TestCase):
  1017.     def setUp(self):
  1018.         self._msg = MIMEText('hello there')
  1019.  
  1020.     def test_types(self):
  1021.         eq = self.assertEqual
  1022.         unless = self.failUnless
  1023.         eq(self._msg.get_type(), 'text/plain')
  1024.         eq(self._msg.get_param('charset'), 'us-ascii')
  1025.         missing = []
  1026.         unless(self._msg.get_param('foobar', missing) is missing)
  1027.         unless(self._msg.get_param('charset', missing, header='foobar')
  1028.                is missing)
  1029.  
  1030.     def test_payload(self):
  1031.         self.assertEqual(self._msg.get_payload(), 'hello there')
  1032.         self.failUnless(not self._msg.is_multipart())
  1033.  
  1034.     def test_charset(self):
  1035.         eq = self.assertEqual
  1036.         msg = MIMEText('hello there', _charset='us-ascii')
  1037.         eq(msg.get_charset().input_charset, 'us-ascii')
  1038.         eq(msg['content-type'], 'text/plain; charset="us-ascii"')
  1039.  
  1040.  
  1041.  
  1042. # Test a more complicated multipart/mixed type message
  1043. class TestMultipartMixed(TestEmailBase):
  1044.     def setUp(self):
  1045.         fp = openfile('PyBanner048.gif')
  1046.         try:
  1047.             data = fp.read()
  1048.         finally:
  1049.             fp.close()
  1050.  
  1051.         container = MIMEBase('multipart', 'mixed', boundary='BOUNDARY')
  1052.         image = MIMEImage(data, name='dingusfish.gif')
  1053.         image.add_header('content-disposition', 'attachment',
  1054.                          filename='dingusfish.gif')
  1055.         intro = MIMEText('''\
  1056. Hi there,
  1057.  
  1058. This is the dingus fish.
  1059. ''')
  1060.         container.attach(intro)
  1061.         container.attach(image)
  1062.         container['From'] = 'Barry <barry@digicool.com>'
  1063.         container['To'] = 'Dingus Lovers <cravindogs@cravindogs.com>'
  1064.         container['Subject'] = 'Here is your dingus fish'
  1065.  
  1066.         now = 987809702.54848599
  1067.         timetuple = time.localtime(now)
  1068.         if timetuple[-1] == 0:
  1069.             tzsecs = time.timezone
  1070.         else:
  1071.             tzsecs = time.altzone
  1072.         if tzsecs > 0:
  1073.             sign = '-'
  1074.         else:
  1075.             sign = '+'
  1076.         tzoffset = ' %s%04d' % (sign, tzsecs / 36)
  1077.         container['Date'] = time.strftime(
  1078.             '%a, %d %b %Y %H:%M:%S',
  1079.             time.localtime(now)) + tzoffset
  1080.         self._msg = container
  1081.         self._im = image
  1082.         self._txt = intro
  1083.  
  1084.     def test_hierarchy(self):
  1085.         # convenience
  1086.         eq = self.assertEqual
  1087.         unless = self.failUnless
  1088.         raises = self.assertRaises
  1089.         # tests
  1090.         m = self._msg
  1091.         unless(m.is_multipart())
  1092.         eq(m.get_type(), 'multipart/mixed')
  1093.         eq(len(m.get_payload()), 2)
  1094.         raises(IndexError, m.get_payload, 2)
  1095.         m0 = m.get_payload(0)
  1096.         m1 = m.get_payload(1)
  1097.         unless(m0 is self._txt)
  1098.         unless(m1 is self._im)
  1099.         eq(m.get_payload(), [m0, m1])
  1100.         unless(not m0.is_multipart())
  1101.         unless(not m1.is_multipart())
  1102.  
  1103.     def test_no_parts_in_a_multipart(self):
  1104.         outer = MIMEBase('multipart', 'mixed')
  1105.         outer['Subject'] = 'A subject'
  1106.         outer['To'] = 'aperson@dom.ain'
  1107.         outer['From'] = 'bperson@dom.ain'
  1108.         outer.preamble = ''
  1109.         outer.epilogue = ''
  1110.         outer.set_boundary('BOUNDARY')
  1111.         msg = MIMEText('hello world')
  1112.         self.assertEqual(outer.as_string(), '''\
  1113. Content-Type: multipart/mixed; boundary="BOUNDARY"
  1114. MIME-Version: 1.0
  1115. Subject: A subject
  1116. To: aperson@dom.ain
  1117. From: bperson@dom.ain
  1118.  
  1119. --BOUNDARY
  1120.  
  1121.  
  1122. --BOUNDARY--
  1123. ''')
  1124.  
  1125.     def test_one_part_in_a_multipart(self):
  1126.         eq = self.ndiffAssertEqual
  1127.         outer = MIMEBase('multipart', 'mixed')
  1128.         outer['Subject'] = 'A subject'
  1129.         outer['To'] = 'aperson@dom.ain'
  1130.         outer['From'] = 'bperson@dom.ain'
  1131.         outer.preamble = ''
  1132.         outer.epilogue = ''
  1133.         outer.set_boundary('BOUNDARY')
  1134.         msg = MIMEText('hello world')
  1135.         outer.attach(msg)
  1136.         eq(outer.as_string(), '''\
  1137. Content-Type: multipart/mixed; boundary="BOUNDARY"
  1138. MIME-Version: 1.0
  1139. Subject: A subject
  1140. To: aperson@dom.ain
  1141. From: bperson@dom.ain
  1142.  
  1143. --BOUNDARY
  1144. Content-Type: text/plain; charset="us-ascii"
  1145. MIME-Version: 1.0
  1146. Content-Transfer-Encoding: 7bit
  1147.  
  1148. hello world
  1149. --BOUNDARY--
  1150. ''')
  1151.  
  1152.     def test_seq_parts_in_a_multipart(self):
  1153.         eq = self.ndiffAssertEqual
  1154.         outer = MIMEBase('multipart', 'mixed')
  1155.         outer['Subject'] = 'A subject'
  1156.         outer['To'] = 'aperson@dom.ain'
  1157.         outer['From'] = 'bperson@dom.ain'
  1158.         outer.preamble = ''
  1159.         outer.epilogue = ''
  1160.         msg = MIMEText('hello world')
  1161.         outer.attach(msg)
  1162.         outer.set_boundary('BOUNDARY')
  1163.         eq(outer.as_string(), '''\
  1164. Content-Type: multipart/mixed; boundary="BOUNDARY"
  1165. MIME-Version: 1.0
  1166. Subject: A subject
  1167. To: aperson@dom.ain
  1168. From: bperson@dom.ain
  1169.  
  1170. --BOUNDARY
  1171. Content-Type: text/plain; charset="us-ascii"
  1172. MIME-Version: 1.0
  1173. Content-Transfer-Encoding: 7bit
  1174.  
  1175. hello world
  1176. --BOUNDARY--
  1177. ''')
  1178.  
  1179.  
  1180.  
  1181. # Test some badly formatted messages
  1182. class TestNonConformant(TestEmailBase):
  1183.     def test_parse_missing_minor_type(self):
  1184.         eq = self.assertEqual
  1185.         msg = self._msgobj('msg_14.txt')
  1186.         eq(msg.get_type(), 'text')
  1187.         eq(msg.get_main_type(), None)
  1188.         eq(msg.get_subtype(), None)
  1189.  
  1190.     def test_bogus_boundary(self):
  1191.         fp = openfile(findfile('msg_15.txt'))
  1192.         try:
  1193.             data = fp.read()
  1194.         finally:
  1195.             fp.close()
  1196.         p = Parser(strict=True)
  1197.         # Note, under a future non-strict parsing mode, this would parse the
  1198.         # message into the intended message tree.
  1199.         self.assertRaises(Errors.BoundaryError, p.parsestr, data)
  1200.  
  1201.     def test_multipart_no_boundary(self):
  1202.         fp = openfile(findfile('msg_25.txt'))
  1203.         try:
  1204.             self.assertRaises(Errors.BoundaryError,
  1205.                               email.message_from_file, fp)
  1206.         finally:
  1207.             fp.close()
  1208.  
  1209.     def test_invalid_content_type(self):
  1210.         eq = self.assertEqual
  1211.         neq = self.ndiffAssertEqual
  1212.         msg = Message()
  1213.         # RFC 2045, $5.2 says invalid yields text/plain
  1214.         msg['Content-Type'] = 'text'
  1215.         eq(msg.get_content_maintype(), 'text')
  1216.         eq(msg.get_content_subtype(), 'plain')
  1217.         eq(msg.get_content_type(), 'text/plain')
  1218.         # Clear the old value and try something /really/ invalid
  1219.         del msg['content-type']
  1220.         msg['Content-Type'] = 'foo'
  1221.         eq(msg.get_content_maintype(), 'text')
  1222.         eq(msg.get_content_subtype(), 'plain')
  1223.         eq(msg.get_content_type(), 'text/plain')
  1224.         # Still, make sure that the message is idempotently generated
  1225.         s = StringIO()
  1226.         g = Generator(s)
  1227.         g.flatten(msg)
  1228.         neq(s.getvalue(), 'Content-Type: foo\n\n')
  1229.  
  1230.     def test_no_start_boundary(self):
  1231.         eq = self.ndiffAssertEqual
  1232.         msg = self._msgobj('msg_31.txt')
  1233.         eq(msg.get_payload(), """\
  1234. --BOUNDARY
  1235. Content-Type: text/plain
  1236.  
  1237. message 1
  1238.  
  1239. --BOUNDARY
  1240. Content-Type: text/plain
  1241.  
  1242. message 2
  1243.  
  1244. --BOUNDARY--
  1245. """)
  1246.  
  1247.     def test_no_separating_blank_line(self):
  1248.         eq = self.ndiffAssertEqual
  1249.         msg = self._msgobj('msg_35.txt')
  1250.         eq(msg.as_string(), """\
  1251. From: aperson@dom.ain
  1252. To: bperson@dom.ain
  1253. Subject: here's something interesting
  1254.  
  1255. counter to RFC 2822, there's no separating newline here
  1256. """)
  1257.         # strict=True should raise an exception
  1258.         self.assertRaises(Errors.HeaderParseError,
  1259.                           self._msgobj, 'msg_35.txt', True)
  1260.  
  1261.  
  1262.  
  1263. # Test RFC 2047 header encoding and decoding
  1264. class TestRFC2047(unittest.TestCase):
  1265.     def test_iso_8859_1(self):
  1266.         eq = self.assertEqual
  1267.         s = '=?iso-8859-1?q?this=20is=20some=20text?='
  1268.         eq(Utils.decode(s), 'this is some text')
  1269.         s = '=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?='
  1270.         eq(Utils.decode(s), u'Keld J\xf8rn Simonsen')
  1271.         s = '=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=' \
  1272.             '=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?='
  1273.         eq(Utils.decode(s), 'If you can read this you understand the example.')
  1274.         s = '=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?='
  1275.         eq(Utils.decode(s),
  1276.            u'\u05dd\u05d5\u05dc\u05e9 \u05df\u05d1 \u05d9\u05dc\u05d8\u05e4\u05e0')
  1277.         s = '=?iso-8859-1?q?this=20is?= =?iso-8859-1?q?some=20text?='
  1278.         eq(Utils.decode(s), u'this issome text')
  1279.         s = '=?iso-8859-1?q?this=20is_?= =?iso-8859-1?q?some=20text?='
  1280.         eq(Utils.decode(s), u'this is some text')
  1281.  
  1282.     def test_encode_header(self):
  1283.         eq = self.assertEqual
  1284.         s = 'this is some text'
  1285.         eq(Utils.encode(s), '=?iso-8859-1?q?this=20is=20some=20text?=')
  1286.         s = 'Keld_J\xf8rn_Simonsen'
  1287.         eq(Utils.encode(s), '=?iso-8859-1?q?Keld_J=F8rn_Simonsen?=')
  1288.         s1 = 'If you can read this yo'
  1289.         s2 = 'u understand the example.'
  1290.         eq(Utils.encode(s1, encoding='b'),
  1291.            '=?iso-8859-1?b?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=')
  1292.         eq(Utils.encode(s2, charset='iso-8859-2', encoding='b'),
  1293.            '=?iso-8859-2?b?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=')
  1294.  
  1295.     def test_rfc2047_multiline(self):
  1296.         eq = self.assertEqual
  1297.         s = """Re: =?mac-iceland?q?r=8Aksm=9Arg=8Cs?= baz
  1298.  foo bar =?mac-iceland?q?r=8Aksm=9Arg=8Cs?="""
  1299.         dh = decode_header(s)
  1300.         eq(dh, [
  1301.             ('Re:', None),
  1302.             ('r\x8aksm\x9arg\x8cs', 'mac-iceland'),
  1303.             ('baz foo bar', None),
  1304.             ('r\x8aksm\x9arg\x8cs', 'mac-iceland')])
  1305.         eq(str(make_header(dh)),
  1306.            """Re: =?mac-iceland?q?r=8Aksm=9Arg=8Cs?= baz foo bar
  1307.  =?mac-iceland?q?r=8Aksm=9Arg=8Cs?=""")
  1308.  
  1309.     def test_whitespace_eater_unicode(self):
  1310.         eq = self.assertEqual
  1311.         s = '=?ISO-8859-1?Q?Andr=E9?= Pirard <pirard@dom.ain>'
  1312.         dh = decode_header(s)
  1313.         eq(dh, [('Andr\xe9', 'iso-8859-1'), ('Pirard <pirard@dom.ain>', None)])
  1314.         # Python 2.1's unicode() builtin doesn't call the object's
  1315.         # __unicode__() method.  Use the following alternative instead.
  1316.         #hu = unicode(make_header(dh)).encode('latin-1')
  1317.         hu = make_header(dh).__unicode__().encode('latin-1')
  1318.         eq(hu, 'Andr\xe9 Pirard <pirard@dom.ain>')
  1319.  
  1320.     def test_whitespace_eater_unicode_2(self):
  1321.         eq = self.assertEqual
  1322.         s = 'The =?iso-8859-1?b?cXVpY2sgYnJvd24gZm94?= jumped over the =?iso-8859-1?b?bGF6eSBkb2c=?='
  1323.         dh = decode_header(s)
  1324.         eq(dh, [('The', None), ('quick brown fox', 'iso-8859-1'),
  1325.                 ('jumped over the', None), ('lazy dog', 'iso-8859-1')])
  1326.         hu = make_header(dh).__unicode__()
  1327.         eq(hu, u'The quick brown fox jumped over the lazy dog')
  1328.  
  1329.  
  1330.  
  1331. # Test the MIMEMessage class
  1332. class TestMIMEMessage(TestEmailBase):
  1333.     def setUp(self):
  1334.         fp = openfile('msg_11.txt')
  1335.         try:
  1336.             self._text = fp.read()
  1337.         finally:
  1338.             fp.close()
  1339.  
  1340.     def test_type_error(self):
  1341.         self.assertRaises(TypeError, MIMEMessage, 'a plain string')
  1342.  
  1343.     def test_valid_argument(self):
  1344.         eq = self.assertEqual
  1345.         unless = self.failUnless
  1346.         subject = 'A sub-message'
  1347.         m = Message()
  1348.         m['Subject'] = subject
  1349.         r = MIMEMessage(m)
  1350.         eq(r.get_type(), 'message/rfc822')
  1351.         payload = r.get_payload()
  1352.         unless(type(payload), ListType)
  1353.         eq(len(payload), 1)
  1354.         subpart = payload[0]
  1355.         unless(subpart is m)
  1356.         eq(subpart['subject'], subject)
  1357.  
  1358.     def test_bad_multipart(self):
  1359.         eq = self.assertEqual
  1360.         msg1 = Message()
  1361.         msg1['Subject'] = 'subpart 1'
  1362.         msg2 = Message()
  1363.         msg2['Subject'] = 'subpart 2'
  1364.         r = MIMEMessage(msg1)
  1365.         self.assertRaises(Errors.MultipartConversionError, r.attach, msg2)
  1366.  
  1367.     def test_generate(self):
  1368.         # First craft the message to be encapsulated
  1369.         m = Message()
  1370.         m['Subject'] = 'An enclosed message'
  1371.         m.set_payload('Here is the body of the message.\n')
  1372.         r = MIMEMessage(m)
  1373.         r['Subject'] = 'The enclosing message'
  1374.         s = StringIO()
  1375.         g = Generator(s)
  1376.         g.flatten(r)
  1377.         self.assertEqual(s.getvalue(), """\
  1378. Content-Type: message/rfc822
  1379. MIME-Version: 1.0
  1380. Subject: The enclosing message
  1381.  
  1382. Subject: An enclosed message
  1383.  
  1384. Here is the body of the message.
  1385. """)
  1386.  
  1387.     def test_parse_message_rfc822(self):
  1388.         eq = self.assertEqual
  1389.         unless = self.failUnless
  1390.         msg = self._msgobj('msg_11.txt')
  1391.         eq(msg.get_type(), 'message/rfc822')
  1392.         payload = msg.get_payload()
  1393.         unless(isinstance(payload, ListType))
  1394.         eq(len(payload), 1)
  1395.         submsg = payload[0]
  1396.         self.failUnless(isinstance(submsg, Message))
  1397.         eq(submsg['subject'], 'An enclosed message')
  1398.         eq(submsg.get_payload(), 'Here is the body of the message.\n')
  1399.  
  1400.     def test_dsn(self):
  1401.         eq = self.assertEqual
  1402.         unless = self.failUnless
  1403.         # msg 16 is a Delivery Status Notification, see RFC 1894
  1404.         msg = self._msgobj('msg_16.txt')
  1405.         eq(msg.get_type(), 'multipart/report')
  1406.         unless(msg.is_multipart())
  1407.         eq(len(msg.get_payload()), 3)
  1408.         # Subpart 1 is a text/plain, human readable section
  1409.         subpart = msg.get_payload(0)
  1410.         eq(subpart.get_type(), 'text/plain')
  1411.         eq(subpart.get_payload(), """\
  1412. This report relates to a message you sent with the following header fields:
  1413.  
  1414.   Message-id: <002001c144a6$8752e060$56104586@oxy.edu>
  1415.   Date: Sun, 23 Sep 2001 20:10:55 -0700
  1416.   From: "Ian T. Henry" <henryi@oxy.edu>
  1417.   To: SoCal Raves <scr@socal-raves.org>
  1418.   Subject: [scr] yeah for Ians!!
  1419.  
  1420. Your message cannot be delivered to the following recipients:
  1421.  
  1422.   Recipient address: jangel1@cougar.noc.ucla.edu
  1423.   Reason: recipient reached disk quota
  1424.  
  1425. """)
  1426.         # Subpart 2 contains the machine parsable DSN information.  It
  1427.         # consists of two blocks of headers, represented by two nested Message
  1428.         # objects.
  1429.         subpart = msg.get_payload(1)
  1430.         eq(subpart.get_type(), 'message/delivery-status')
  1431.         eq(len(subpart.get_payload()), 2)
  1432.         # message/delivery-status should treat each block as a bunch of
  1433.         # headers, i.e. a bunch of Message objects.
  1434.         dsn1 = subpart.get_payload(0)
  1435.         unless(isinstance(dsn1, Message))
  1436.         eq(dsn1['original-envelope-id'], '0GK500B4HD0888@cougar.noc.ucla.edu')
  1437.         eq(dsn1.get_param('dns', header='reporting-mta'), '')
  1438.         # Try a missing one <wink>
  1439.         eq(dsn1.get_param('nsd', header='reporting-mta'), None)
  1440.         dsn2 = subpart.get_payload(1)
  1441.         unless(isinstance(dsn2, Message))
  1442.         eq(dsn2['action'], 'failed')
  1443.         eq(dsn2.get_params(header='original-recipient'),
  1444.            [('rfc822', ''), ('jangel1@cougar.noc.ucla.edu', '')])
  1445.         eq(dsn2.get_param('rfc822', header='final-recipient'), '')
  1446.         # Subpart 3 is the original message
  1447.         subpart = msg.get_payload(2)
  1448.         eq(subpart.get_type(), 'message/rfc822')
  1449.         payload = subpart.get_payload()
  1450.         unless(isinstance(payload, ListType))
  1451.         eq(len(payload), 1)
  1452.         subsubpart = payload[0]
  1453.         unless(isinstance(subsubpart, Message))
  1454.         eq(subsubpart.get_type(), 'text/plain')
  1455.         eq(subsubpart['message-id'],
  1456.            '<002001c144a6$8752e060$56104586@oxy.edu>')
  1457.  
  1458.     def test_epilogue(self):
  1459.         eq = self.ndiffAssertEqual
  1460.         fp = openfile('msg_21.txt')
  1461.         try:
  1462.             text = fp.read()
  1463.         finally:
  1464.             fp.close()
  1465.         msg = Message()
  1466.         msg['From'] = 'aperson@dom.ain'
  1467.         msg['To'] = 'bperson@dom.ain'
  1468.         msg['Subject'] = 'Test'
  1469.         msg.preamble = 'MIME message\n'
  1470.         msg.epilogue = 'End of MIME message\n'
  1471.         msg1 = MIMEText('One')
  1472.         msg2 = MIMEText('Two')
  1473.         msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY')
  1474.         msg.attach(msg1)
  1475.         msg.attach(msg2)
  1476.         sfp = StringIO()
  1477.         g = Generator(sfp)
  1478.         g.flatten(msg)
  1479.         eq(sfp.getvalue(), text)
  1480.  
  1481.     def test_no_nl_preamble(self):
  1482.         eq = self.ndiffAssertEqual
  1483.         msg = Message()
  1484.         msg['From'] = 'aperson@dom.ain'
  1485.         msg['To'] = 'bperson@dom.ain'
  1486.         msg['Subject'] = 'Test'
  1487.         msg.preamble = 'MIME message'
  1488.         msg.epilogue = ''
  1489.         msg1 = MIMEText('One')
  1490.         msg2 = MIMEText('Two')
  1491.         msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY')
  1492.         msg.attach(msg1)
  1493.         msg.attach(msg2)
  1494.         eq(msg.as_string(), """\
  1495. From: aperson@dom.ain
  1496. To: bperson@dom.ain
  1497. Subject: Test
  1498. Content-Type: multipart/mixed; boundary="BOUNDARY"
  1499.  
  1500. MIME message
  1501. --BOUNDARY
  1502. Content-Type: text/plain; charset="us-ascii"
  1503. MIME-Version: 1.0
  1504. Content-Transfer-Encoding: 7bit
  1505.  
  1506. One
  1507. --BOUNDARY
  1508. Content-Type: text/plain; charset="us-ascii"
  1509. MIME-Version: 1.0
  1510. Content-Transfer-Encoding: 7bit
  1511.  
  1512. Two
  1513. --BOUNDARY--
  1514. """)
  1515.  
  1516.     def test_default_type(self):
  1517.         eq = self.assertEqual
  1518.         fp = openfile('msg_30.txt')
  1519.         try:
  1520.             msg = email.message_from_file(fp)
  1521.         finally:
  1522.             fp.close()
  1523.         container1 = msg.get_payload(0)
  1524.         eq(container1.get_default_type(), 'message/rfc822')
  1525.         eq(container1.get_type(), None)
  1526.         container2 = msg.get_payload(1)
  1527.         eq(container2.get_default_type(), 'message/rfc822')
  1528.         eq(container2.get_type(), None)
  1529.         container1a = container1.get_payload(0)
  1530.         eq(container1a.get_default_type(), 'text/plain')
  1531.         eq(container1a.get_type(), 'text/plain')
  1532.         container2a = container2.get_payload(0)
  1533.         eq(container2a.get_default_type(), 'text/plain')
  1534.         eq(container2a.get_type(), 'text/plain')
  1535.  
  1536.     def test_default_type_with_explicit_container_type(self):
  1537.         eq = self.assertEqual
  1538.         fp = openfile('msg_28.txt')
  1539.         try:
  1540.             msg = email.message_from_file(fp)
  1541.         finally:
  1542.             fp.close()
  1543.         container1 = msg.get_payload(0)
  1544.         eq(container1.get_default_type(), 'message/rfc822')
  1545.         eq(container1.get_type(), 'message/rfc822')
  1546.         container2 = msg.get_payload(1)
  1547.         eq(container2.get_default_type(), 'message/rfc822')
  1548.         eq(container2.get_type(), 'message/rfc822')
  1549.         container1a = container1.get_payload(0)
  1550.         eq(container1a.get_default_type(), 'text/plain')
  1551.         eq(container1a.get_type(), 'text/plain')
  1552.         container2a = container2.get_payload(0)
  1553.         eq(container2a.get_default_type(), 'text/plain')
  1554.         eq(container2a.get_type(), 'text/plain')
  1555.  
  1556.     def test_default_type_non_parsed(self):
  1557.         eq = self.assertEqual
  1558.         neq = self.ndiffAssertEqual
  1559.         # Set up container
  1560.         container = MIMEMultipart('digest', 'BOUNDARY')
  1561.         container.epilogue = '\n'
  1562.         # Set up subparts
  1563.         subpart1a = MIMEText('message 1\n')
  1564.         subpart2a = MIMEText('message 2\n')
  1565.         subpart1 = MIMEMessage(subpart1a)
  1566.         subpart2 = MIMEMessage(subpart2a)
  1567.         container.attach(subpart1)
  1568.         container.attach(subpart2)
  1569.         eq(subpart1.get_type(), 'message/rfc822')
  1570.         eq(subpart1.get_default_type(), 'message/rfc822')
  1571.         eq(subpart2.get_type(), 'message/rfc822')
  1572.         eq(subpart2.get_default_type(), 'message/rfc822')
  1573.         neq(container.as_string(0), '''\
  1574. Content-Type: multipart/digest; boundary="BOUNDARY"
  1575. MIME-Version: 1.0
  1576.  
  1577. --BOUNDARY
  1578. Content-Type: message/rfc822
  1579. MIME-Version: 1.0
  1580.  
  1581. Content-Type: text/plain; charset="us-ascii"
  1582. MIME-Version: 1.0
  1583. Content-Transfer-Encoding: 7bit
  1584.  
  1585. message 1
  1586.  
  1587. --BOUNDARY
  1588. Content-Type: message/rfc822
  1589. MIME-Version: 1.0
  1590.  
  1591. Content-Type: text/plain; charset="us-ascii"
  1592. MIME-Version: 1.0
  1593. Content-Transfer-Encoding: 7bit
  1594.  
  1595. message 2
  1596.  
  1597. --BOUNDARY--
  1598. ''')
  1599.         del subpart1['content-type']
  1600.         del subpart1['mime-version']
  1601.         del subpart2['content-type']
  1602.         del subpart2['mime-version']
  1603.         eq(subpart1.get_type(), None)
  1604.         eq(subpart1.get_default_type(), 'message/rfc822')
  1605.         eq(subpart2.get_type(), None)
  1606.         eq(subpart2.get_default_type(), 'message/rfc822')
  1607.         neq(container.as_string(0), '''\
  1608. Content-Type: multipart/digest; boundary="BOUNDARY"
  1609. MIME-Version: 1.0
  1610.  
  1611. --BOUNDARY
  1612.  
  1613. Content-Type: text/plain; charset="us-ascii"
  1614. MIME-Version: 1.0
  1615. Content-Transfer-Encoding: 7bit
  1616.  
  1617. message 1
  1618.  
  1619. --BOUNDARY
  1620.  
  1621. Content-Type: text/plain; charset="us-ascii"
  1622. MIME-Version: 1.0
  1623. Content-Transfer-Encoding: 7bit
  1624.  
  1625. message 2
  1626.  
  1627. --BOUNDARY--
  1628. ''')
  1629.  
  1630.  
  1631.  
  1632. # A general test of parser->model->generator idempotency.  IOW, read a message
  1633. # in, parse it into a message object tree, then without touching the tree,
  1634. # regenerate the plain text.  The original text and the transformed text
  1635. # should be identical.  Note: that we ignore the Unix-From since that may
  1636. # contain a changed date.
  1637. class TestIdempotent(TestEmailBase):
  1638.     def _msgobj(self, filename):
  1639.         fp = openfile(filename)
  1640.         try:
  1641.             data = fp.read()
  1642.         finally:
  1643.             fp.close()
  1644.         msg = email.message_from_string(data)
  1645.         return msg, data
  1646.  
  1647.     def _idempotent(self, msg, text):
  1648.         eq = self.ndiffAssertEqual
  1649.         s = StringIO()
  1650.         g = Generator(s, maxheaderlen=0)
  1651.         g.flatten(msg)
  1652.         eq(text, s.getvalue())
  1653.  
  1654.     def test_parse_text_message(self):
  1655.         eq = self.assertEquals
  1656.         msg, text = self._msgobj('msg_01.txt')
  1657.         eq(msg.get_type(), 'text/plain')
  1658.         eq(msg.get_main_type(), 'text')
  1659.         eq(msg.get_subtype(), 'plain')
  1660.         eq(msg.get_params()[1], ('charset', 'us-ascii'))
  1661.         eq(msg.get_param('charset'), 'us-ascii')
  1662.         eq(msg.preamble, None)
  1663.         eq(msg.epilogue, None)
  1664.         self._idempotent(msg, text)
  1665.  
  1666.     def test_parse_untyped_message(self):
  1667.         eq = self.assertEquals
  1668.         msg, text = self._msgobj('msg_03.txt')
  1669.         eq(msg.get_type(), None)
  1670.         eq(msg.get_params(), None)
  1671.         eq(msg.get_param('charset'), None)
  1672.         self._idempotent(msg, text)
  1673.  
  1674.     def test_simple_multipart(self):
  1675.         msg, text = self._msgobj('msg_04.txt')
  1676.         self._idempotent(msg, text)
  1677.  
  1678.     def test_MIME_digest(self):
  1679.         msg, text = self._msgobj('msg_02.txt')
  1680.         self._idempotent(msg, text)
  1681.  
  1682.     def test_long_header(self):
  1683.         msg, text = self._msgobj('msg_27.txt')
  1684.         self._idempotent(msg, text)
  1685.  
  1686.     def test_MIME_digest_with_part_headers(self):
  1687.         msg, text = self._msgobj('msg_28.txt')
  1688.         self._idempotent(msg, text)
  1689.  
  1690.     def test_mixed_with_image(self):
  1691.         msg, text = self._msgobj('msg_06.txt')
  1692.         self._idempotent(msg, text)
  1693.  
  1694.     def test_multipart_report(self):
  1695.         msg, text = self._msgobj('msg_05.txt')
  1696.         self._idempotent(msg, text)
  1697.  
  1698.     def test_dsn(self):
  1699.         msg, text = self._msgobj('msg_16.txt')
  1700.         self._idempotent(msg, text)
  1701.  
  1702.     def test_preamble_epilogue(self):
  1703.         msg, text = self._msgobj('msg_21.txt')
  1704.         self._idempotent(msg, text)
  1705.  
  1706.     def test_multipart_one_part(self):
  1707.         msg, text = self._msgobj('msg_23.txt')
  1708.         self._idempotent(msg, text)
  1709.  
  1710.     def test_multipart_no_parts(self):
  1711.         msg, text = self._msgobj('msg_24.txt')
  1712.         self._idempotent(msg, text)
  1713.  
  1714.     def test_no_start_boundary(self):
  1715.         msg, text = self._msgobj('msg_31.txt')
  1716.         self._idempotent(msg, text)
  1717.  
  1718.     def test_rfc2231_charset(self):
  1719.         msg, text = self._msgobj('msg_32.txt')
  1720.         self._idempotent(msg, text)
  1721.  
  1722.     def test_more_rfc2231_parameters(self):
  1723.         msg, text = self._msgobj('msg_33.txt')
  1724.         self._idempotent(msg, text)
  1725.  
  1726.     def test_text_plain_in_a_multipart_digest(self):
  1727.         msg, text = self._msgobj('msg_34.txt')
  1728.         self._idempotent(msg, text)
  1729.  
  1730.     def test_content_type(self):
  1731.         eq = self.assertEquals
  1732.         unless = self.failUnless
  1733.         # Get a message object and reset the seek pointer for other tests
  1734.         msg, text = self._msgobj('msg_05.txt')
  1735.         eq(msg.get_type(), 'multipart/report')
  1736.         # Test the Content-Type: parameters
  1737.         params = {}
  1738.         for pk, pv in msg.get_params():
  1739.             params[pk] = pv
  1740.         eq(params['report-type'], 'delivery-status')
  1741.         eq(params['boundary'], 'D1690A7AC1.996856090/mail.example.com')
  1742.         eq(msg.preamble, 'This is a MIME-encapsulated message.\n\n')
  1743.         eq(msg.epilogue, '\n\n')
  1744.         eq(len(msg.get_payload()), 3)
  1745.         # Make sure the subparts are what we expect
  1746.         msg1 = msg.get_payload(0)
  1747.         eq(msg1.get_type(), 'text/plain')
  1748.         eq(msg1.get_payload(), 'Yadda yadda yadda\n')
  1749.         msg2 = msg.get_payload(1)
  1750.         eq(msg2.get_type(), None)
  1751.         eq(msg2.get_payload(), 'Yadda yadda yadda\n')
  1752.         msg3 = msg.get_payload(2)
  1753.         eq(msg3.get_type(), 'message/rfc822')
  1754.         self.failUnless(isinstance(msg3, Message))
  1755.         payload = msg3.get_payload()
  1756.         unless(isinstance(payload, ListType))
  1757.         eq(len(payload), 1)
  1758.         msg4 = payload[0]
  1759.         unless(isinstance(msg4, Message))
  1760.         eq(msg4.get_payload(), 'Yadda yadda yadda\n')
  1761.  
  1762.     def test_parser(self):
  1763.         eq = self.assertEquals
  1764.         unless = self.failUnless
  1765.         msg, text = self._msgobj('msg_06.txt')
  1766.         # Check some of the outer headers
  1767.         eq(msg.get_type(), 'message/rfc822')
  1768.         # Make sure the payload is a list of exactly one sub-Message, and that
  1769.         # that submessage has a type of text/plain
  1770.         payload = msg.get_payload()
  1771.         unless(isinstance(payload, ListType))
  1772.         eq(len(payload), 1)
  1773.         msg1 = payload[0]
  1774.         self.failUnless(isinstance(msg1, Message))
  1775.         eq(msg1.get_type(), 'text/plain')
  1776.         self.failUnless(isinstance(msg1.get_payload(), StringType))
  1777.         eq(msg1.get_payload(), '\n')
  1778.  
  1779.  
  1780.  
  1781. # Test various other bits of the package's functionality
  1782. class TestMiscellaneous(unittest.TestCase):
  1783.     def test_message_from_string(self):
  1784.         fp = openfile('msg_01.txt')
  1785.         try:
  1786.             text = fp.read()
  1787.         finally:
  1788.             fp.close()
  1789.         msg = email.message_from_string(text)
  1790.         s = StringIO()
  1791.         # Don't wrap/continue long headers since we're trying to test
  1792.         # idempotency.
  1793.         g = Generator(s, maxheaderlen=0)
  1794.         g.flatten(msg)
  1795.         self.assertEqual(text, s.getvalue())
  1796.  
  1797.     def test_message_from_file(self):
  1798.         fp = openfile('msg_01.txt')
  1799.         try:
  1800.             text = fp.read()
  1801.             fp.seek(0)
  1802.             msg = email.message_from_file(fp)
  1803.             s = StringIO()
  1804.             # Don't wrap/continue long headers since we're trying to test
  1805.             # idempotency.
  1806.             g = Generator(s, maxheaderlen=0)
  1807.             g.flatten(msg)
  1808.             self.assertEqual(text, s.getvalue())
  1809.         finally:
  1810.             fp.close()
  1811.  
  1812.     def test_message_from_string_with_class(self):
  1813.         unless = self.failUnless
  1814.         fp = openfile('msg_01.txt')
  1815.         try:
  1816.             text = fp.read()
  1817.         finally:
  1818.             fp.close()
  1819.         # Create a subclass
  1820.         class MyMessage(Message):
  1821.             pass
  1822.  
  1823.         msg = email.message_from_string(text, MyMessage)
  1824.         unless(isinstance(msg, MyMessage))
  1825.         # Try something more complicated
  1826.         fp = openfile('msg_02.txt')
  1827.         try:
  1828.             text = fp.read()
  1829.         finally:
  1830.             fp.close()
  1831.         msg = email.message_from_string(text, MyMessage)
  1832.         for subpart in msg.walk():
  1833.             unless(isinstance(subpart, MyMessage))
  1834.  
  1835.     def test_message_from_file_with_class(self):
  1836.         unless = self.failUnless
  1837.         # Create a subclass
  1838.         class MyMessage(Message):
  1839.             pass
  1840.  
  1841.         fp = openfile('msg_01.txt')
  1842.         try:
  1843.             msg = email.message_from_file(fp, MyMessage)
  1844.         finally:
  1845.             fp.close()
  1846.         unless(isinstance(msg, MyMessage))
  1847.         # Try something more complicated
  1848.         fp = openfile('msg_02.txt')
  1849.         try:
  1850.             msg = email.message_from_file(fp, MyMessage)
  1851.         finally:
  1852.             fp.close()
  1853.         for subpart in msg.walk():
  1854.             unless(isinstance(subpart, MyMessage))
  1855.  
  1856.     def test__all__(self):
  1857.         module = __import__('email')
  1858.         all = module.__all__
  1859.         all.sort()
  1860.         self.assertEqual(all, ['Charset', 'Encoders', 'Errors', 'Generator',
  1861.                                'Header', 'Iterators', 'MIMEAudio', 'MIMEBase',
  1862.                                'MIMEImage', 'MIMEMessage', 'MIMEMultipart',
  1863.                                'MIMENonMultipart', 'MIMEText', 'Message',
  1864.                                'Parser', 'Utils', 'base64MIME',
  1865.                                'message_from_file', 'message_from_string',
  1866.                                'quopriMIME'])
  1867.  
  1868.     def test_formatdate(self):
  1869.         now = time.time()
  1870.         self.assertEqual(Utils.parsedate(Utils.formatdate(now))[:6],
  1871.                          time.gmtime(now)[:6])
  1872.  
  1873.     def test_formatdate_localtime(self):
  1874.         now = time.time()
  1875.         self.assertEqual(
  1876.             Utils.parsedate(Utils.formatdate(now, localtime=True))[:6],
  1877.             time.localtime(now)[:6])
  1878.  
  1879.     def test_parsedate_none(self):
  1880.         self.assertEqual(Utils.parsedate(''), None)
  1881.  
  1882.     def test_parsedate_compact(self):
  1883.         # The FWS after the comma is optional
  1884.         self.assertEqual(Utils.parsedate('Wed,3 Apr 2002 14:58:26 +0800'),
  1885.                          Utils.parsedate('Wed, 3 Apr 2002 14:58:26 +0800'))
  1886.  
  1887.     def test_parsedate_no_dayofweek(self):
  1888.         eq = self.assertEqual
  1889.         eq(Utils.parsedate_tz('25 Feb 2003 13:47:26 -0800'),
  1890.            (2003, 2, 25, 13, 47, 26, 0, 0, 0, -28800))
  1891.  
  1892.     def test_parsedate_compact_no_dayofweek(self):
  1893.         eq = self.assertEqual
  1894.         eq(Utils.parsedate_tz('5 Feb 2003 13:47:26 -0800'),
  1895.            (2003, 2, 5, 13, 47, 26, 0, 0, 0, -28800))
  1896.  
  1897.     def test_parseaddr_empty(self):
  1898.         self.assertEqual(Utils.parseaddr('<>'), ('', ''))
  1899.         self.assertEqual(Utils.formataddr(Utils.parseaddr('<>')), '')
  1900.  
  1901.     def test_noquote_dump(self):
  1902.         self.assertEqual(
  1903.             Utils.formataddr(('A Silly Person', 'person@dom.ain')),
  1904.             'A Silly Person <person@dom.ain>')
  1905.  
  1906.     def test_escape_dump(self):
  1907.         self.assertEqual(
  1908.             Utils.formataddr(('A (Very) Silly Person', 'person@dom.ain')),
  1909.             r'"A \(Very\) Silly Person" <person@dom.ain>')
  1910.         a = r'A \(Special\) Person'
  1911.         b = 'person@dom.ain'
  1912.         self.assertEqual(Utils.parseaddr(Utils.formataddr((a, b))), (a, b))
  1913.  
  1914.     def test_escape_backslashes(self):
  1915.         self.assertEqual(
  1916.             Utils.formataddr(('Arthur \Backslash\ Foobar', 'person@dom.ain')),
  1917.             r'"Arthur \\Backslash\\ Foobar" <person@dom.ain>')
  1918.         a = r'Arthur \Backslash\ Foobar'
  1919.         b = 'person@dom.ain'
  1920.         self.assertEqual(Utils.parseaddr(Utils.formataddr((a, b))), (a, b))
  1921.  
  1922.     def test_name_with_dot(self):
  1923.         x = 'John X. Doe <jxd@example.com>'
  1924.         y = '"John X. Doe" <jxd@example.com>'
  1925.         a, b = ('John X. Doe', 'jxd@example.com')
  1926.         self.assertEqual(Utils.parseaddr(x), (a, b))
  1927.         self.assertEqual(Utils.parseaddr(y), (a, b))
  1928.         # formataddr() quotes the name if there's a dot in it
  1929.         self.assertEqual(Utils.formataddr((a, b)), y)
  1930.  
  1931.     def test_quote_dump(self):
  1932.         self.assertEqual(
  1933.             Utils.formataddr(('A Silly; Person', 'person@dom.ain')),
  1934.             r'"A Silly; Person" <person@dom.ain>')
  1935.  
  1936.     def test_fix_eols(self):
  1937.         eq = self.assertEqual
  1938.         eq(Utils.fix_eols('hello'), 'hello')
  1939.         eq(Utils.fix_eols('hello\n'), 'hello\r\n')
  1940.         eq(Utils.fix_eols('hello\r'), 'hello\r\n')
  1941.         eq(Utils.fix_eols('hello\r\n'), 'hello\r\n')
  1942.         eq(Utils.fix_eols('hello\n\r'), 'hello\r\n\r\n')
  1943.  
  1944.     def test_charset_richcomparisons(self):
  1945.         eq = self.assertEqual
  1946.         ne = self.failIfEqual
  1947.         cset1 = Charset()
  1948.         cset2 = Charset()
  1949.         eq(cset1, 'us-ascii')
  1950.         eq(cset1, 'US-ASCII')
  1951.         eq(cset1, 'Us-AsCiI')
  1952.         eq('us-ascii', cset1)
  1953.         eq('US-ASCII', cset1)
  1954.         eq('Us-AsCiI', cset1)
  1955.         ne(cset1, 'usascii')
  1956.         ne(cset1, 'USASCII')
  1957.         ne(cset1, 'UsAsCiI')
  1958.         ne('usascii', cset1)
  1959.         ne('USASCII', cset1)
  1960.         ne('UsAsCiI', cset1)
  1961.         eq(cset1, cset2)
  1962.         eq(cset2, cset1)
  1963.  
  1964.     def test_getaddresses(self):
  1965.         eq = self.assertEqual
  1966.         eq(Utils.getaddresses(['aperson@dom.ain (Al Person)',
  1967.                                'Bud Person <bperson@dom.ain>']),
  1968.            [('Al Person', 'aperson@dom.ain'),
  1969.             ('Bud Person', 'bperson@dom.ain')])
  1970.  
  1971.     def test_getaddresses_nasty(self):
  1972.         eq = self.assertEqual
  1973.         eq(Utils.getaddresses(['foo: ;']), [('', '')])
  1974.         eq(Utils.getaddresses(
  1975.            ['[]*-- =~$']),
  1976.            [('', ''), ('', ''), ('', '*--')])
  1977.         eq(Utils.getaddresses(
  1978.            ['foo: ;', '"Jason R. Mastaler" <jason@dom.ain>']),
  1979.            [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')])
  1980.  
  1981.     def test_utils_quote_unquote(self):
  1982.         eq = self.assertEqual
  1983.         msg = Message()
  1984.         msg.add_header('content-disposition', 'attachment',
  1985.                        filename='foo\\wacky"name')
  1986.         eq(msg.get_filename(), 'foo\\wacky"name')
  1987.  
  1988.     def test_get_body_encoding_with_bogus_charset(self):
  1989.         charset = Charset('not a charset')
  1990.         self.assertEqual(charset.get_body_encoding(), 'base64')
  1991.  
  1992.     def test_get_body_encoding_with_uppercase_charset(self):
  1993.         eq = self.assertEqual
  1994.         msg = Message()
  1995.         msg['Content-Type'] = 'text/plain; charset=UTF-8'
  1996.         eq(msg['content-type'], 'text/plain; charset=UTF-8')
  1997.         charsets = msg.get_charsets()
  1998.         eq(len(charsets), 1)
  1999.         eq(charsets[0], 'utf-8')
  2000.         charset = Charset(charsets[0])
  2001.         eq(charset.get_body_encoding(), 'base64')
  2002.         msg.set_payload('hello world', charset=charset)
  2003.         eq(msg.get_payload(), 'hello world')
  2004.         eq(msg['content-transfer-encoding'], 'base64')
  2005.         # Try another one
  2006.         msg = Message()
  2007.         msg['Content-Type'] = 'text/plain; charset="US-ASCII"'
  2008.         charsets = msg.get_charsets()
  2009.         eq(len(charsets), 1)
  2010.         eq(charsets[0], 'us-ascii')
  2011.         charset = Charset(charsets[0])
  2012.         eq(charset.get_body_encoding(), Encoders.encode_7or8bit)
  2013.         msg.set_payload('hello world', charset=charset)
  2014.         eq(msg.get_payload(), 'hello world')
  2015.         eq(msg['content-transfer-encoding'], '7bit')
  2016.  
  2017.     def test_charsets_case_insensitive(self):
  2018.         lc = Charset('us-ascii')
  2019.         uc = Charset('US-ASCII')
  2020.         self.assertEqual(lc.get_body_encoding(), uc.get_body_encoding())
  2021.  
  2022.  
  2023.  
  2024. # Test the iterator/generators
  2025. class TestIterators(TestEmailBase):
  2026.     def test_body_line_iterator(self):
  2027.         eq = self.assertEqual
  2028.         # First a simple non-multipart message
  2029.         msg = self._msgobj('msg_01.txt')
  2030.         it = Iterators.body_line_iterator(msg)
  2031.         lines = list(it)
  2032.         eq(len(lines), 6)
  2033.         eq(EMPTYSTRING.join(lines), msg.get_payload())
  2034.         # Now a more complicated multipart
  2035.         msg = self._msgobj('msg_02.txt')
  2036.         it = Iterators.body_line_iterator(msg)
  2037.         lines = list(it)
  2038.         eq(len(lines), 43)
  2039.         fp = openfile('msg_19.txt')
  2040.         try:
  2041.             eq(EMPTYSTRING.join(lines), fp.read())
  2042.         finally:
  2043.             fp.close()
  2044.  
  2045.     def test_typed_subpart_iterator(self):
  2046.         eq = self.assertEqual
  2047.         msg = self._msgobj('msg_04.txt')
  2048.         it = Iterators.typed_subpart_iterator(msg, 'text')
  2049.         lines = []
  2050.         subparts = 0
  2051.         for subpart in it:
  2052.             subparts += 1
  2053.             lines.append(subpart.get_payload())
  2054.         eq(subparts, 2)
  2055.         eq(EMPTYSTRING.join(lines), """\
  2056. a simple kind of mirror
  2057. to reflect upon our own
  2058. a simple kind of mirror
  2059. to reflect upon our own
  2060. """)
  2061.  
  2062.     def test_typed_subpart_iterator_default_type(self):
  2063.         eq = self.assertEqual
  2064.         msg = self._msgobj('msg_03.txt')
  2065.         it = Iterators.typed_subpart_iterator(msg, 'text', 'plain')
  2066.         lines = []
  2067.         subparts = 0
  2068.         for subpart in it:
  2069.             subparts += 1
  2070.             lines.append(subpart.get_payload())
  2071.         eq(subparts, 1)
  2072.         eq(EMPTYSTRING.join(lines), """\
  2073.  
  2074. Hi,
  2075.  
  2076. Do you like this message?
  2077.  
  2078. -Me
  2079. """)
  2080.  
  2081.  
  2082.  
  2083. class TestParsers(TestEmailBase):
  2084.     def test_header_parser(self):
  2085.         eq = self.assertEqual
  2086.         # Parse only the headers of a complex multipart MIME document
  2087.         fp = openfile('msg_02.txt')
  2088.         try:
  2089.             msg = HeaderParser().parse(fp)
  2090.         finally:
  2091.             fp.close()
  2092.         eq(msg['from'], 'ppp-request@zzz.org')
  2093.         eq(msg['to'], 'ppp@zzz.org')
  2094.         eq(msg.get_type(), 'multipart/mixed')
  2095.         eq(msg.is_multipart(), 0)
  2096.         self.failUnless(isinstance(msg.get_payload(), StringType))
  2097.  
  2098.     def test_whitespace_continuaton(self):
  2099.         eq = self.assertEqual
  2100.         # This message contains a line after the Subject: header that has only
  2101.         # whitespace, but it is not empty!
  2102.         msg = email.message_from_string("""\
  2103. From: aperson@dom.ain
  2104. To: bperson@dom.ain
  2105. Subject: the next line has a space on it
  2106. \x20
  2107. Date: Mon, 8 Apr 2002 15:09:19 -0400
  2108. Message-ID: spam
  2109.  
  2110. Here's the message body
  2111. """)
  2112.         eq(msg['subject'], 'the next line has a space on it\n ')
  2113.         eq(msg['message-id'], 'spam')
  2114.         eq(msg.get_payload(), "Here's the message body\n")
  2115.  
  2116.     def test_crlf_separation(self):
  2117.         eq = self.assertEqual
  2118.         fp = openfile('msg_26.txt', mode='rb')
  2119.         try:
  2120.             msg = Parser().parse(fp)
  2121.         finally:
  2122.             fp.close()
  2123.         eq(len(msg.get_payload()), 2)
  2124.         part1 = msg.get_payload(0)
  2125.         eq(part1.get_type(), 'text/plain')
  2126.         eq(part1.get_payload(), 'Simple email with attachment.\r\n\r\n')
  2127.         part2 = msg.get_payload(1)
  2128.         eq(part2.get_type(), 'application/riscos')
  2129.  
  2130.     def test_multipart_digest_with_extra_mime_headers(self):
  2131.         eq = self.assertEqual
  2132.         neq = self.ndiffAssertEqual
  2133.         fp = openfile('msg_28.txt')
  2134.         try:
  2135.             msg = email.message_from_file(fp)
  2136.         finally:
  2137.             fp.close()
  2138.         # Structure is:
  2139.         # multipart/digest
  2140.         #   message/rfc822
  2141.         #     text/plain
  2142.         #   message/rfc822
  2143.         #     text/plain
  2144.         eq(msg.is_multipart(), 1)
  2145.         eq(len(msg.get_payload()), 2)
  2146.         part1 = msg.get_payload(0)
  2147.         eq(part1.get_type(), 'message/rfc822')
  2148.         eq(part1.is_multipart(), 1)
  2149.         eq(len(part1.get_payload()), 1)
  2150.         part1a = part1.get_payload(0)
  2151.         eq(part1a.is_multipart(), 0)
  2152.         eq(part1a.get_type(), 'text/plain')
  2153.         neq(part1a.get_payload(), 'message 1\n')
  2154.         # next message/rfc822
  2155.         part2 = msg.get_payload(1)
  2156.         eq(part2.get_type(), 'message/rfc822')
  2157.         eq(part2.is_multipart(), 1)
  2158.         eq(len(part2.get_payload()), 1)
  2159.         part2a = part2.get_payload(0)
  2160.         eq(part2a.is_multipart(), 0)
  2161.         eq(part2a.get_type(), 'text/plain')
  2162.         neq(part2a.get_payload(), 'message 2\n')
  2163.  
  2164.     def test_three_lines(self):
  2165.         # A bug report by Andrew McNamara
  2166.         lines = ['From: Andrew Person <aperson@dom.ain',
  2167.                  'Subject: Test',
  2168.                  'Date: Tue, 20 Aug 2002 16:43:45 +1000']
  2169.         msg = email.message_from_string(NL.join(lines))
  2170.         self.assertEqual(msg['date'], 'Tue, 20 Aug 2002 16:43:45 +1000')
  2171.  
  2172.  
  2173.  
  2174. class TestBase64(unittest.TestCase):
  2175.     def test_len(self):
  2176.         eq = self.assertEqual
  2177.         eq(base64MIME.base64_len('hello'),
  2178.            len(base64MIME.encode('hello', eol='')))
  2179.         for size in range(15):
  2180.             if   size == 0 : bsize = 0
  2181.             elif size <= 3 : bsize = 4
  2182.             elif size <= 6 : bsize = 8
  2183.             elif size <= 9 : bsize = 12
  2184.             elif size <= 12: bsize = 16
  2185.             else           : bsize = 20
  2186.             eq(base64MIME.base64_len('x'*size), bsize)
  2187.  
  2188.     def test_decode(self):
  2189.         eq = self.assertEqual
  2190.         eq(base64MIME.decode(''), '')
  2191.         eq(base64MIME.decode('aGVsbG8='), 'hello')
  2192.         eq(base64MIME.decode('aGVsbG8=', 'X'), 'hello')
  2193.         eq(base64MIME.decode('aGVsbG8NCndvcmxk\n', 'X'), 'helloXworld')
  2194.  
  2195.     def test_encode(self):
  2196.         eq = self.assertEqual
  2197.         eq(base64MIME.encode(''), '')
  2198.         eq(base64MIME.encode('hello'), 'aGVsbG8=\n')
  2199.         # Test the binary flag
  2200.         eq(base64MIME.encode('hello\n'), 'aGVsbG8K\n')
  2201.         eq(base64MIME.encode('hello\n', 0), 'aGVsbG8NCg==\n')
  2202.         # Test the maxlinelen arg
  2203.         eq(base64MIME.encode('xxxx ' * 20, maxlinelen=40), """\
  2204. eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg
  2205. eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg
  2206. eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg
  2207. eHh4eCB4eHh4IA==
  2208. """)
  2209.         # Test the eol argument
  2210.         eq(base64MIME.encode('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\
  2211. eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r
  2212. eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r
  2213. eHh4eCB4eHh4IHh4eHggeHh4eCB4eHh4IHh4eHgg\r
  2214. eHh4eCB4eHh4IA==\r
  2215. """)
  2216.  
  2217.     def test_header_encode(self):
  2218.         eq = self.assertEqual
  2219.         he = base64MIME.header_encode
  2220.         eq(he('hello'), '=?iso-8859-1?b?aGVsbG8=?=')
  2221.         eq(he('hello\nworld'), '=?iso-8859-1?b?aGVsbG8NCndvcmxk?=')
  2222.         # Test the charset option
  2223.         eq(he('hello', charset='iso-8859-2'), '=?iso-8859-2?b?aGVsbG8=?=')
  2224.         # Test the keep_eols flag
  2225.         eq(he('hello\nworld', keep_eols=True),
  2226.            '=?iso-8859-1?b?aGVsbG8Kd29ybGQ=?=')
  2227.         # Test the maxlinelen argument
  2228.         eq(he('xxxx ' * 20, maxlinelen=40), """\
  2229. =?iso-8859-1?b?eHh4eCB4eHh4IHh4eHggeHg=?=
  2230.  =?iso-8859-1?b?eHggeHh4eCB4eHh4IHh4eHg=?=
  2231.  =?iso-8859-1?b?IHh4eHggeHh4eCB4eHh4IHg=?=
  2232.  =?iso-8859-1?b?eHh4IHh4eHggeHh4eCB4eHg=?=
  2233.  =?iso-8859-1?b?eCB4eHh4IHh4eHggeHh4eCA=?=
  2234.  =?iso-8859-1?b?eHh4eCB4eHh4IHh4eHgg?=""")
  2235.         # Test the eol argument
  2236.         eq(he('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\
  2237. =?iso-8859-1?b?eHh4eCB4eHh4IHh4eHggeHg=?=\r
  2238.  =?iso-8859-1?b?eHggeHh4eCB4eHh4IHh4eHg=?=\r
  2239.  =?iso-8859-1?b?IHh4eHggeHh4eCB4eHh4IHg=?=\r
  2240.  =?iso-8859-1?b?eHh4IHh4eHggeHh4eCB4eHg=?=\r
  2241.  =?iso-8859-1?b?eCB4eHh4IHh4eHggeHh4eCA=?=\r
  2242.  =?iso-8859-1?b?eHh4eCB4eHh4IHh4eHgg?=""")
  2243.  
  2244.  
  2245.  
  2246. class TestQuopri(unittest.TestCase):
  2247.     def setUp(self):
  2248.         self.hlit = [chr(x) for x in range(ord('a'), ord('z')+1)] + \
  2249.                     [chr(x) for x in range(ord('A'), ord('Z')+1)] + \
  2250.                     [chr(x) for x in range(ord('0'), ord('9')+1)] + \
  2251.                     ['!', '*', '+', '-', '/', ' ']
  2252.         self.hnon = [chr(x) for x in range(256) if chr(x) not in self.hlit]
  2253.         assert len(self.hlit) + len(self.hnon) == 256
  2254.         self.blit = [chr(x) for x in range(ord(' '), ord('~')+1)] + ['\t']
  2255.         self.blit.remove('=')
  2256.         self.bnon = [chr(x) for x in range(256) if chr(x) not in self.blit]
  2257.         assert len(self.blit) + len(self.bnon) == 256
  2258.  
  2259.     def test_header_quopri_check(self):
  2260.         for c in self.hlit:
  2261.             self.failIf(quopriMIME.header_quopri_check(c))
  2262.         for c in self.hnon:
  2263.             self.failUnless(quopriMIME.header_quopri_check(c))
  2264.  
  2265.     def test_body_quopri_check(self):
  2266.         for c in self.blit:
  2267.             self.failIf(quopriMIME.body_quopri_check(c))
  2268.         for c in self.bnon:
  2269.             self.failUnless(quopriMIME.body_quopri_check(c))
  2270.  
  2271.     def test_header_quopri_len(self):
  2272.         eq = self.assertEqual
  2273.         hql = quopriMIME.header_quopri_len
  2274.         enc = quopriMIME.header_encode
  2275.         for s in ('hello', 'h@e@l@l@o@'):
  2276.             # Empty charset and no line-endings.  7 == RFC chrome
  2277.             eq(hql(s), len(enc(s, charset='', eol=''))-7)
  2278.         for c in self.hlit:
  2279.             eq(hql(c), 1)
  2280.         for c in self.hnon:
  2281.             eq(hql(c), 3)
  2282.  
  2283.     def test_body_quopri_len(self):
  2284.         eq = self.assertEqual
  2285.         bql = quopriMIME.body_quopri_len
  2286.         for c in self.blit:
  2287.             eq(bql(c), 1)
  2288.         for c in self.bnon:
  2289.             eq(bql(c), 3)
  2290.  
  2291.     def test_quote_unquote_idempotent(self):
  2292.         for x in range(256):
  2293.             c = chr(x)
  2294.             self.assertEqual(quopriMIME.unquote(quopriMIME.quote(c)), c)
  2295.  
  2296.     def test_header_encode(self):
  2297.         eq = self.assertEqual
  2298.         he = quopriMIME.header_encode
  2299.         eq(he('hello'), '=?iso-8859-1?q?hello?=')
  2300.         eq(he('hello\nworld'), '=?iso-8859-1?q?hello=0D=0Aworld?=')
  2301.         # Test the charset option
  2302.         eq(he('hello', charset='iso-8859-2'), '=?iso-8859-2?q?hello?=')
  2303.         # Test the keep_eols flag
  2304.         eq(he('hello\nworld', keep_eols=True), '=?iso-8859-1?q?hello=0Aworld?=')
  2305.         # Test a non-ASCII character
  2306.         eq(he('hello\xc7there'), '=?iso-8859-1?q?hello=C7there?=')
  2307.         # Test the maxlinelen argument
  2308.         eq(he('xxxx ' * 20, maxlinelen=40), """\
  2309. =?iso-8859-1?q?xxxx_xxxx_xxxx_xxxx_xx?=
  2310.  =?iso-8859-1?q?xx_xxxx_xxxx_xxxx_xxxx?=
  2311.  =?iso-8859-1?q?_xxxx_xxxx_xxxx_xxxx_x?=
  2312.  =?iso-8859-1?q?xxx_xxxx_xxxx_xxxx_xxx?=
  2313.  =?iso-8859-1?q?x_xxxx_xxxx_?=""")
  2314.         # Test the eol argument
  2315.         eq(he('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\
  2316. =?iso-8859-1?q?xxxx_xxxx_xxxx_xxxx_xx?=\r
  2317.  =?iso-8859-1?q?xx_xxxx_xxxx_xxxx_xxxx?=\r
  2318.  =?iso-8859-1?q?_xxxx_xxxx_xxxx_xxxx_x?=\r
  2319.  =?iso-8859-1?q?xxx_xxxx_xxxx_xxxx_xxx?=\r
  2320.  =?iso-8859-1?q?x_xxxx_xxxx_?=""")
  2321.  
  2322.     def test_decode(self):
  2323.         eq = self.assertEqual
  2324.         eq(quopriMIME.decode(''), '')
  2325.         eq(quopriMIME.decode('hello'), 'hello')
  2326.         eq(quopriMIME.decode('hello', 'X'), 'hello')
  2327.         eq(quopriMIME.decode('hello\nworld', 'X'), 'helloXworld')
  2328.  
  2329.     def test_encode(self):
  2330.         eq = self.assertEqual
  2331.         eq(quopriMIME.encode(''), '')
  2332.         eq(quopriMIME.encode('hello'), 'hello')
  2333.         # Test the binary flag
  2334.         eq(quopriMIME.encode('hello\r\nworld'), 'hello\nworld')
  2335.         eq(quopriMIME.encode('hello\r\nworld', 0), 'hello\nworld')
  2336.         # Test the maxlinelen arg
  2337.         eq(quopriMIME.encode('xxxx ' * 20, maxlinelen=40), """\
  2338. xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx=
  2339.  xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxx=
  2340. x xxxx xxxx xxxx xxxx=20""")
  2341.         # Test the eol argument
  2342.         eq(quopriMIME.encode('xxxx ' * 20, maxlinelen=40, eol='\r\n'), """\
  2343. xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx=\r
  2344.  xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxx=\r
  2345. x xxxx xxxx xxxx xxxx=20""")
  2346.         eq(quopriMIME.encode("""\
  2347. one line
  2348.  
  2349. two line"""), """\
  2350. one line
  2351.  
  2352. two line""")
  2353.  
  2354.  
  2355.  
  2356. # Test the Charset class
  2357. class TestCharset(unittest.TestCase):
  2358.     def tearDown(self):
  2359.         from email import Charset as CharsetModule
  2360.         try:
  2361.             del CharsetModule.CHARSETS['fake']
  2362.         except KeyError:
  2363.             pass
  2364.  
  2365.     def test_idempotent(self):
  2366.         eq = self.assertEqual
  2367.         # Make sure us-ascii = no Unicode conversion
  2368.         c = Charset('us-ascii')
  2369.         s = 'Hello World!'
  2370.         sp = c.to_splittable(s)
  2371.         eq(s, c.from_splittable(sp))
  2372.         # test 8-bit idempotency with us-ascii
  2373.         s = '\xa4\xa2\xa4\xa4\xa4\xa6\xa4\xa8\xa4\xaa'
  2374.         sp = c.to_splittable(s)
  2375.         eq(s, c.from_splittable(sp))
  2376.  
  2377.     def test_body_encode(self):
  2378.         eq = self.assertEqual
  2379.         # Try a charset with QP body encoding
  2380.         c = Charset('iso-8859-1')
  2381.         eq('hello w=F6rld', c.body_encode('hello w\xf6rld'))
  2382.         # Try a charset with Base64 body encoding
  2383.         c = Charset('utf-8')
  2384.         eq('aGVsbG8gd29ybGQ=\n', c.body_encode('hello world'))
  2385.         # Try a charset with None body encoding
  2386.         c = Charset('us-ascii')
  2387.         eq('hello world', c.body_encode('hello world'))
  2388.         # Try the convert argument, where input codec <> output codec
  2389.         c = Charset('euc-jp')
  2390.         # With apologies to Tokio Kikuchi ;)
  2391.         try:
  2392.             eq('\x1b$B5FCO;~IW\x1b(B',
  2393.                c.body_encode('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7'))
  2394.             eq('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7',
  2395.                c.body_encode('\xb5\xc6\xc3\xcf\xbb\xfe\xc9\xd7', False))
  2396.         except LookupError:
  2397.             # We probably don't have the Japanese codecs installed
  2398.             pass
  2399.         # Testing SF bug #625509, which we have to fake, since there are no
  2400.         # built-in encodings where the header encoding is QP but the body
  2401.         # encoding is not.
  2402.         from email import Charset as CharsetModule
  2403.         CharsetModule.add_charset('fake', CharsetModule.QP, None)
  2404.         c = Charset('fake')
  2405.         eq('hello w\xf6rld', c.body_encode('hello w\xf6rld'))
  2406.  
  2407.  
  2408.  
  2409. # Test multilingual MIME headers.
  2410. class TestHeader(TestEmailBase):
  2411.     def test_simple(self):
  2412.         eq = self.ndiffAssertEqual
  2413.         h = Header('Hello World!')
  2414.         eq(h.encode(), 'Hello World!')
  2415.         h.append(' Goodbye World!')
  2416.         eq(h.encode(), 'Hello World!  Goodbye World!')
  2417.  
  2418.     def test_simple_surprise(self):
  2419.         eq = self.ndiffAssertEqual
  2420.         h = Header('Hello World!')
  2421.         eq(h.encode(), 'Hello World!')
  2422.         h.append('Goodbye World!')
  2423.         eq(h.encode(), 'Hello World! Goodbye World!')
  2424.  
  2425.     def test_header_needs_no_decoding(self):
  2426.         h = 'no decoding needed'
  2427.         self.assertEqual(decode_header(h), [(h, None)])
  2428.  
  2429.     def test_long(self):
  2430.         h = Header("I am the very model of a modern Major-General; I've information vegetable, animal, and mineral; I know the kings of England, and I quote the fights historical from Marathon to Waterloo, in order categorical; I'm very well acquainted, too, with matters mathematical; I understand equations, both the simple and quadratical; about binomial theorem I'm teeming with a lot o' news, with many cheerful facts about the square of the hypotenuse.",
  2431.                    maxlinelen=76)
  2432.         for l in h.encode(splitchars=' ').split('\n '):
  2433.             self.failUnless(len(l) <= 76)
  2434.  
  2435.     def test_multilingual(self):
  2436.         eq = self.ndiffAssertEqual
  2437.         g = Charset("iso-8859-1")
  2438.         cz = Charset("iso-8859-2")
  2439.         utf8 = Charset("utf-8")
  2440.         g_head = "Die Mieter treten hier ein werden mit einem Foerderband komfortabel den Korridor entlang, an s\xfcdl\xfcndischen Wandgem\xe4lden vorbei, gegen die rotierenden Klingen bef\xf6rdert. "
  2441.         cz_head = "Finan\xe8ni metropole se hroutily pod tlakem jejich d\xf9vtipu.. "
  2442.         utf8_head = u"\u6b63\u78ba\u306b\u8a00\u3046\u3068\u7ffb\u8a33\u306f\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4e00\u90e8\u306f\u30c9\u30a4\u30c4\u8a9e\u3067\u3059\u304c\u3001\u3042\u3068\u306f\u3067\u305f\u3089\u3081\u3067\u3059\u3002\u5b9f\u969b\u306b\u306f\u300cWenn ist das Nunstuck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput.\u300d\u3068\u8a00\u3063\u3066\u3044\u307e\u3059\u3002".encode("utf-8")
  2443.         h = Header(g_head, g)
  2444.         h.append(cz_head, cz)
  2445.         h.append(utf8_head, utf8)
  2446.         enc = h.encode()
  2447.         eq(enc, """\
  2448. =?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerderband_ko?=
  2449.  =?iso-8859-1?q?mfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndischen_Wan?=
  2450.  =?iso-8859-1?q?dgem=E4lden_vorbei=2C_gegen_die_rotierenden_Klingen_bef=F6?=
  2451.  =?iso-8859-1?q?rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_metropole_se_hroutily?=
  2452.  =?iso-8859-2?q?_pod_tlakem_jejich_d=F9vtipu=2E=2E_?= =?utf-8?b?5q2j56K6?=
  2453.  =?utf-8?b?44Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE44G+44Gb44KT44CC?=
  2454.  =?utf-8?b?5LiA6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB44GC44Go44Gv44Gn?=
  2455.  =?utf-8?b?44Gf44KJ44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBpc3QgZGFz?=
  2456.  =?utf-8?q?_Nunstuck_git_und_Slotermeyer=3F_Ja!_Beiherhund_das_Oder_die_Fl?=
  2457.  =?utf-8?b?aXBwZXJ3YWxkdCBnZXJzcHV0LuOAjeOBqOiogOOBo+OBpuOBhOOBvuOBmQ==?=
  2458.  =?utf-8?b?44CC?=""")
  2459.         eq(decode_header(enc),
  2460.            [(g_head, "iso-8859-1"), (cz_head, "iso-8859-2"),
  2461.             (utf8_head, "utf-8")])
  2462.         # Test for conversion to unicode.  BAW: Python 2.1 doesn't support the
  2463.         # __unicode__() protocol, so do things this way for compatibility.
  2464.         ustr = h.__unicode__()
  2465.         # For Python 2.2 and beyond
  2466.         #ustr = unicode(h)
  2467.         eq(ustr.encode('utf-8'),
  2468.            'Die Mieter treten hier ein werden mit einem Foerderband '
  2469.            'komfortabel den Korridor entlang, an s\xc3\xbcdl\xc3\xbcndischen '
  2470.            'Wandgem\xc3\xa4lden vorbei, gegen die rotierenden Klingen '
  2471.            'bef\xc3\xb6rdert. Finan\xc4\x8dni metropole se hroutily pod '
  2472.            'tlakem jejich d\xc5\xafvtipu.. \xe6\xad\xa3\xe7\xa2\xba\xe3\x81'
  2473.            '\xab\xe8\xa8\x80\xe3\x81\x86\xe3\x81\xa8\xe7\xbf\xbb\xe8\xa8\xb3'
  2474.            '\xe3\x81\xaf\xe3\x81\x95\xe3\x82\x8c\xe3\x81\xa6\xe3\x81\x84\xe3'
  2475.            '\x81\xbe\xe3\x81\x9b\xe3\x82\x93\xe3\x80\x82\xe4\xb8\x80\xe9\x83'
  2476.            '\xa8\xe3\x81\xaf\xe3\x83\x89\xe3\x82\xa4\xe3\x83\x84\xe8\xaa\x9e'
  2477.            '\xe3\x81\xa7\xe3\x81\x99\xe3\x81\x8c\xe3\x80\x81\xe3\x81\x82\xe3'
  2478.            '\x81\xa8\xe3\x81\xaf\xe3\x81\xa7\xe3\x81\x9f\xe3\x82\x89\xe3\x82'
  2479.            '\x81\xe3\x81\xa7\xe3\x81\x99\xe3\x80\x82\xe5\xae\x9f\xe9\x9a\x9b'
  2480.            '\xe3\x81\xab\xe3\x81\xaf\xe3\x80\x8cWenn ist das Nunstuck git '
  2481.            'und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt '
  2482.            'gersput.\xe3\x80\x8d\xe3\x81\xa8\xe8\xa8\x80\xe3\x81\xa3\xe3\x81'
  2483.            '\xa6\xe3\x81\x84\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82')
  2484.         # Test make_header()
  2485.         newh = make_header(decode_header(enc))
  2486.         eq(newh, enc)
  2487.  
  2488.     def test_header_ctor_default_args(self):
  2489.         eq = self.ndiffAssertEqual
  2490.         h = Header()
  2491.         eq(h, '')
  2492.         h.append('foo', Charset('iso-8859-1'))
  2493.         eq(h, '=?iso-8859-1?q?foo?=')
  2494.  
  2495.     def test_explicit_maxlinelen(self):
  2496.         eq = self.ndiffAssertEqual
  2497.         hstr = 'A very long line that must get split to something other than at the 76th character boundary to test the non-default behavior'
  2498.         h = Header(hstr)
  2499.         eq(h.encode(), '''\
  2500. A very long line that must get split to something other than at the 76th
  2501.  character boundary to test the non-default behavior''')
  2502.         h = Header(hstr, header_name='Subject')
  2503.         eq(h.encode(), '''\
  2504. A very long line that must get split to something other than at the
  2505.  76th character boundary to test the non-default behavior''')
  2506.         h = Header(hstr, maxlinelen=1024, header_name='Subject')
  2507.         eq(h.encode(), hstr)
  2508.  
  2509.     def test_us_ascii_header(self):
  2510.         eq = self.assertEqual
  2511.         s = 'hello'
  2512.         x = decode_header(s)
  2513.         eq(x, [('hello', None)])
  2514.         h = make_header(x)
  2515.         eq(s, h.encode())
  2516.  
  2517.     def test_string_charset(self):
  2518.         eq = self.assertEqual
  2519.         h = Header()
  2520.         h.append('hello', 'iso-8859-1')
  2521.         eq(h, '=?iso-8859-1?q?hello?=')
  2522.  
  2523. ##    def test_unicode_error(self):
  2524. ##        raises = self.assertRaises
  2525. ##        raises(UnicodeError, Header, u'[P\xf6stal]', 'us-ascii')
  2526. ##        raises(UnicodeError, Header, '[P\xf6stal]', 'us-ascii')
  2527. ##        h = Header()
  2528. ##        raises(UnicodeError, h.append, u'[P\xf6stal]', 'us-ascii')
  2529. ##        raises(UnicodeError, h.append, '[P\xf6stal]', 'us-ascii')
  2530. ##        raises(UnicodeError, Header, u'\u83ca\u5730\u6642\u592b', 'iso-8859-1')
  2531.  
  2532.     def test_utf8_shortest(self):
  2533.         eq = self.assertEqual
  2534.         h = Header(u'p\xf6stal', 'utf-8')
  2535.         eq(h.encode(), '=?utf-8?q?p=C3=B6stal?=')
  2536.         h = Header(u'\u83ca\u5730\u6642\u592b', 'utf-8')
  2537.         eq(h.encode(), '=?utf-8?b?6I+K5Zyw5pmC5aSr?=')
  2538.  
  2539.     def test_bad_8bit_header(self):
  2540.         raises = self.assertRaises
  2541.         eq = self.assertEqual
  2542.         x = 'Ynwp4dUEbay Auction Semiar- No Charge \x96 Earn Big'
  2543.         raises(UnicodeError, Header, x)
  2544.         h = Header()
  2545.         raises(UnicodeError, h.append, x)
  2546.         eq(str(Header(x, errors='replace')), x)
  2547.         h.append(x, errors='replace')
  2548.         eq(str(h), x)
  2549.  
  2550.     def test_encoded_adjacent_nonencoded(self):
  2551.         eq = self.assertEqual
  2552.         h = Header()
  2553.         h.append('hello', 'iso-8859-1')
  2554.         h.append('world')
  2555.         s = h.encode()
  2556.         eq(s, '=?iso-8859-1?q?hello?= world')
  2557.         h = make_header(decode_header(s))
  2558.         eq(h.encode(), s)
  2559.  
  2560.     def test_whitespace_eater(self):
  2561.         eq = self.assertEqual
  2562.         s = 'Subject: =?koi8-r?b?8NLP18XSy8EgzsEgxsnOwczYztk=?= =?koi8-r?q?=CA?= zz.'
  2563.         parts = decode_header(s)
  2564.         eq(parts, [('Subject:', None), ('\xf0\xd2\xcf\xd7\xc5\xd2\xcb\xc1 \xce\xc1 \xc6\xc9\xce\xc1\xcc\xd8\xce\xd9\xca', 'koi8-r'), ('zz.', None)])
  2565.         hdr = make_header(parts)
  2566.         eq(hdr.encode(),
  2567.            'Subject: =?koi8-r?b?8NLP18XSy8EgzsEgxsnOwczYztnK?= zz.')
  2568.  
  2569.     def test_broken_base64_header(self):
  2570.         raises = self.assertRaises
  2571.         s = 'Subject: =?EUC-KR?B?CSixpLDtKSC/7Liuvsax4iC6uLmwMcijIKHaILzSwd/H0SC8+LCjwLsgv7W/+Mj3IQ?='
  2572.         raises(Errors.HeaderParseError, decode_header, s)
  2573.  
  2574.  
  2575.  
  2576. # Test RFC 2231 header parameters (en/de)coding
  2577. class TestRFC2231(TestEmailBase):
  2578.     def test_get_param(self):
  2579.         eq = self.assertEqual
  2580.         msg = self._msgobj('msg_29.txt')
  2581.         eq(msg.get_param('title'),
  2582.            ('us-ascii', 'en', 'This is even more ***fun*** isn\'t it!'))
  2583.         eq(msg.get_param('title', unquote=False),
  2584.            ('us-ascii', 'en', '"This is even more ***fun*** isn\'t it!"'))
  2585.  
  2586.     def test_set_param(self):
  2587.         eq = self.assertEqual
  2588.         msg = Message()
  2589.         msg.set_param('title', 'This is even more ***fun*** isn\'t it!',
  2590.                       charset='us-ascii')
  2591.         eq(msg.get_param('title'),
  2592.            ('us-ascii', '', 'This is even more ***fun*** isn\'t it!'))
  2593.         msg.set_param('title', 'This is even more ***fun*** isn\'t it!',
  2594.                       charset='us-ascii', language='en')
  2595.         eq(msg.get_param('title'),
  2596.            ('us-ascii', 'en', 'This is even more ***fun*** isn\'t it!'))
  2597.         msg = self._msgobj('msg_01.txt')
  2598.         msg.set_param('title', 'This is even more ***fun*** isn\'t it!',
  2599.                       charset='us-ascii', language='en')
  2600.         eq(msg.as_string(), """\
  2601. Return-Path: <bbb@zzz.org>
  2602. Delivered-To: bbb@zzz.org
  2603. Received: by mail.zzz.org (Postfix, from userid 889)
  2604. \tid 27CEAD38CC; Fri,  4 May 2001 14:05:44 -0400 (EDT)
  2605. MIME-Version: 1.0
  2606. Content-Transfer-Encoding: 7bit
  2607. Message-ID: <15090.61304.110929.45684@aaa.zzz.org>
  2608. From: bbb@ddd.com (John X. Doe)
  2609. To: bbb@zzz.org
  2610. Subject: This is a test message
  2611. Date: Fri, 4 May 2001 14:05:44 -0400
  2612. Content-Type: text/plain; charset=us-ascii;
  2613. \ttitle*="us-ascii'en'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20isn%27t%20it%21"
  2614.  
  2615.  
  2616. Hi,
  2617.  
  2618. Do you like this message?
  2619.  
  2620. -Me
  2621. """)
  2622.  
  2623.     def test_del_param(self):
  2624.         eq = self.ndiffAssertEqual
  2625.         msg = self._msgobj('msg_01.txt')
  2626.         msg.set_param('foo', 'bar', charset='us-ascii', language='en')
  2627.         msg.set_param('title', 'This is even more ***fun*** isn\'t it!',
  2628.             charset='us-ascii', language='en')
  2629.         msg.del_param('foo', header='Content-Type')
  2630.         eq(msg.as_string(), """\
  2631. Return-Path: <bbb@zzz.org>
  2632. Delivered-To: bbb@zzz.org
  2633. Received: by mail.zzz.org (Postfix, from userid 889)
  2634. \tid 27CEAD38CC; Fri,  4 May 2001 14:05:44 -0400 (EDT)
  2635. MIME-Version: 1.0
  2636. Content-Transfer-Encoding: 7bit
  2637. Message-ID: <15090.61304.110929.45684@aaa.zzz.org>
  2638. From: bbb@ddd.com (John X. Doe)
  2639. To: bbb@zzz.org
  2640. Subject: This is a test message
  2641. Date: Fri, 4 May 2001 14:05:44 -0400
  2642. Content-Type: text/plain; charset="us-ascii";
  2643. \ttitle*="us-ascii'en'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20isn%27t%20it%21"
  2644.  
  2645.  
  2646. Hi,
  2647.  
  2648. Do you like this message?
  2649.  
  2650. -Me
  2651. """)
  2652.  
  2653.     def test_rfc2231_get_content_charset(self):
  2654.         eq = self.assertEqual
  2655.         msg = self._msgobj('msg_32.txt')
  2656.         eq(msg.get_content_charset(), 'us-ascii')
  2657.  
  2658.     def test_rfc2231_no_language_or_charset(self):
  2659.         m = '''\
  2660. Content-Transfer-Encoding: 8bit
  2661. Content-Disposition: inline; filename="file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEMP_nsmail.htm"
  2662. Content-Type: text/html; NAME*0=file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEM; NAME*1=P_nsmail.htm
  2663.  
  2664. '''
  2665.         msg = email.message_from_string(m)
  2666.         self.assertEqual(msg.get_param('NAME'),
  2667.                          (None, None, 'file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEMP_nsmail.htm'))
  2668.  
  2669.     def test_rfc2231_no_language_or_charset_in_filename(self):
  2670.         m = '''\
  2671. Content-Disposition: inline;
  2672. \tfilename*0="This%20is%20even%20more%20";
  2673. \tfilename*1="%2A%2A%2Afun%2A%2A%2A%20";
  2674. \tfilename*2="is it not.pdf"
  2675.  
  2676. '''
  2677.         msg = email.message_from_string(m)
  2678.         self.assertEqual(msg.get_filename(),
  2679.                          'This is even more ***fun*** is it not.pdf')
  2680.  
  2681.     def test_rfc2231_no_language_or_charset_in_boundary(self):
  2682.         m = '''\
  2683. Content-Type: multipart/alternative;
  2684. \tboundary*0="This%20is%20even%20more%20";
  2685. \tboundary*1="%2A%2A%2Afun%2A%2A%2A%20";
  2686. \tboundary*2="is it not.pdf"
  2687.  
  2688. '''
  2689.         msg = email.message_from_string(m)
  2690.         self.assertEqual(msg.get_boundary(),
  2691.                          'This is even more ***fun*** is it not.pdf')
  2692.  
  2693.     def test_rfc2231_no_language_or_charset_in_charset(self):
  2694.         # This is a nonsensical charset value, but tests the code anyway
  2695.         m = '''\
  2696. Content-Type: text/plain;
  2697. \tcharset*0="This%20is%20even%20more%20";
  2698. \tcharset*1="%2A%2A%2Afun%2A%2A%2A%20";
  2699. \tcharset*2="is it not.pdf"
  2700.  
  2701. '''
  2702.         msg = email.message_from_string(m)
  2703.         self.assertEqual(msg.get_content_charset(),
  2704.                          'this is even more ***fun*** is it not.pdf')
  2705.  
  2706.  
  2707.  
  2708. def _testclasses():
  2709.     mod = sys.modules[__name__]
  2710.     return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')]
  2711.  
  2712.  
  2713. def suite():
  2714.     suite = unittest.TestSuite()
  2715.     for testclass in _testclasses():
  2716.         suite.addTest(unittest.makeSuite(testclass))
  2717.     return suite
  2718.  
  2719.  
  2720. def test_main():
  2721.     for testclass in _testclasses():
  2722.         run_unittest(testclass)
  2723.  
  2724.  
  2725.  
  2726. if __name__ == '__main__':
  2727.     unittest.main(defaultTest='suite')
  2728.